<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>http://wiki.fhem.de/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Alkazaa</id>
	<title>FHEMWiki - Benutzerbeiträge [de]</title>
	<link rel="self" type="application/atom+xml" href="http://wiki.fhem.de/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Alkazaa"/>
	<link rel="alternate" type="text/html" href="http://wiki.fhem.de/wiki/Spezial:Beitr%C3%A4ge/Alkazaa"/>
	<updated>2026-04-18T15:19:02Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.43.6</generator>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=BYDBox&amp;diff=40951</id>
		<title>BYDBox</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=BYDBox&amp;diff=40951"/>
		<updated>2026-04-12T09:33:49Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Unterstützung für die BYD Battery Box&lt;br /&gt;
|ModType=x&lt;br /&gt;
|ModForumArea=Solaranlagen&lt;br /&gt;
|ModFTopic=121643&lt;br /&gt;
|ModTechName=23_BYDBox.pm&lt;br /&gt;
|ModOwner={{Link2FU|36612|MiniBlister}}&lt;br /&gt;
}}&lt;br /&gt;
Das Modul [[BYDBox]] erlaubt es, die Battery Box des chinesischen Herstellers [https://www.byd.com BYD] in FHEM einzubinden.&lt;br /&gt;
&lt;br /&gt;
Die aktuelle Version steht z.Zt. (12.04.2026) in [https://forum.fhem.de/index.php?msg=1349323 diesem] Forenbeitrag. &lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen ==&lt;br /&gt;
&lt;br /&gt;
== Unterstützte Speicherkonfigurationen ==&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [https://www.byd.com Webseite des Herstellers BYD]&lt;br /&gt;
* [[Solaranlage Komplettbeispiel Fronius BYD]] Anwendungsbeispiel und ergänzende Darstellungen der Daten über DOIF`s : Übersicht und einzelne Zellen&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Elektromobilität]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40744</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40744"/>
		<updated>2026-01-17T14:25:38Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* Anwendungsbeispiel */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|Diese Wiki Seite ist noch in Bearbeitung! &amp;lt;br&amp;gt;Siehe auch {{Link2Forum|Topic=142165|LinkText=HTTPMOD: Aktueller Strompreis EEX Strombörse Leipzig abfragen}}}}&lt;br /&gt;
[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=142165|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Voraussetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Voraussetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Voraussetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente. &lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&amp;lt;div style=&amp;quot;max-height:150px; overflow:auto; padding:4px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries €/MWh prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
\&lt;br /&gt;
A reread every 15 min extracts the EPEX-price of the next quarter hour from the data lists in readings\&lt;br /&gt;
EPEX_prices and EPEX_timetags, using the code in reading02RecombineExpr. \&lt;br /&gt;
Within reading02RecombineExpr, &amp;quot;get $name EPEX_prices&amp;quot; is issued at the beginning of every second odd \&lt;br /&gt;
hour in order to keep EPEX_prices and EPEX_timetags up-to-date. \&lt;br /&gt;
Note that the reread itself does not perform an explicit request to api.energy-charts.info, except \&lt;br /&gt;
for a dummy request to localhost. This above strategy minimizes the number of external requests, \&lt;br /&gt;
in order to avoid occasional timeouts as much as possible.\&lt;br /&gt;
\&lt;br /&gt;
no_of_data, the length of the data lists in EPEX_prices and EPEX_timetags is 96 after midnight, and it \&lt;br /&gt;
changes to 192 in the afternoon when the dayahead prices are available.\&lt;br /&gt;
\&lt;br /&gt;
An event-on-change-reading for no_of_data can be used by a FileLog device to save the new data.&lt;br /&gt;
&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
if ($h % 2 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
    &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Code für device &#039;&#039;FileLog_EPEX_Boersenpreise&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define FileLog_EPEX_Boersenpreise FileLog ./log/EPEX_Boersenpreise.log EPEX_Boersenpreise:no_of_data:.192.*&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise outputFormat {my @prices=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;my @times=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;return join &amp;quot;&amp;quot;, map {strftime(&amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;, localtime($times[$_])).&amp;quot; &amp;quot;.&amp;quot;EPEX_Boersenpreise&amp;quot;.&amp;quot; EPEX_price $prices[$_]\n&amp;quot;} $#times - 95 .. $#times}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit einem at-device kann man die Länge der Daten im Log-File begrenzen, in diesem Beispiel auf drei Tage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define trim_EPEX_Logfile at *00:02:12 {`tail -n 288 ./log/EPEX_Boersenpreise.log &amp;gt; ./log/file.tmp &amp;amp;&amp;amp; mv ./log/file.tmp ./log/EPEX_Boersenpreise.log`;;fhem(&amp;quot;set FileLog_EPEX_Boersenpreise reopen&amp;quot;)}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Anwendungsbeispiel===&lt;br /&gt;
Im Beispiel sind der viertelstündliche Strombezug (falls mitgeloggt) und der viertelstündliche EPEX-Preis am 11.1.2026 (vormittags) dargestellt:&lt;br /&gt;
[[Datei:Bezug-EPEX-Preis.png|600px|rahmenlos|links]]&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot; /&amp;gt;&lt;br /&gt;
Der SVG-Plot hat das &#039;&#039;fixedrange&#039;&#039; Attribut &amp;lt;code&amp;gt;&amp;lt;small&amp;gt;3days 1&amp;lt;/small&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Strompreis]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40739</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40739"/>
		<updated>2026-01-16T08:19:55Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* Anwendungsbeispiel */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|Diese Wiki Seite ist noch in Bearbeitung! &amp;lt;br&amp;gt;Siehe auch {{Link2Forum|Topic=142165|LinkText=HTTPMOD: Aktueller Strompreis EEX Strombörse Leipzig abfragen}}}}&lt;br /&gt;
[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=142165|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Voraussetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Voraussetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Voraussetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente. &lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&amp;lt;div style=&amp;quot;max-height:150px; overflow:auto; padding:4px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries €/MWh prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
\&lt;br /&gt;
A reread every 15 min extracts the EPEX-price of the next quarter hour from the data lists in readings\&lt;br /&gt;
EPEX_prices and EPEX_timetags, using the code in reading02RecombineExpr. \&lt;br /&gt;
Within reading02RecombineExpr, &amp;quot;get $name EPEX_prices&amp;quot; is issued at the beginning of every second odd \&lt;br /&gt;
hour in order to keep EPEX_prices and EPEX_timetags up-to-date. \&lt;br /&gt;
Note that the reread itself does not perform an explicit request to api.energy-charts.info, except \&lt;br /&gt;
for a dummy request to localhost. This above strategy minimizes the number of external requests, \&lt;br /&gt;
in order to avoid occasional timeouts as much as possible.\&lt;br /&gt;
\&lt;br /&gt;
no_of_data, the length of the data lists in EPEX_prices and EPEX_timetags is 96 after midnight, and it \&lt;br /&gt;
changes to 192 in the afternoon when the dayahead prices are available.\&lt;br /&gt;
\&lt;br /&gt;
An event-on-change-reading for no_of_data can be used by a FileLog device to save the new data.&lt;br /&gt;
&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
if ($h % 2 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
    &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Code für device &#039;&#039;FileLog_EPEX_Boersenpreise&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define FileLog_EPEX_Boersenpreise FileLog ./log/EPEX_Boersenpreise.log EPEX_Boersenpreise:no_of_data:.192.*&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise outputFormat {my @prices=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;my @times=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;return join &amp;quot;&amp;quot;, map {strftime(&amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;, localtime($times[$_])).&amp;quot; &amp;quot;.&amp;quot;EPEX_Boersenpreise&amp;quot;.&amp;quot; EPEX_price $prices[$_]\n&amp;quot;} $#times - 95 .. $#times}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit einem at-device kann man die Länge der Daten im Log-File begrenzen, in diesem Beispiel auf drei Tage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define trim_EPEX_Logfile at *00:02:12 {`tail -n 288 ./log/EPEX_Boersenpreise.log &amp;gt; ./log/file.tmp &amp;amp;&amp;amp; mv ./log/file.tmp ./log/EPEX_Boersenpreise.log`;;fhem(&amp;quot;set FileLog_EPEX_Boersenpreise reopen&amp;quot;)}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Anwendungsbeispiel===&lt;br /&gt;
Im Beispiel sind der viertelstündliche Strombezug (falls mitgeloggt) und der viertelstündliche EPEX-Preis am 11.1.2026 (vormittags) dargestellt:&lt;br /&gt;
[[Datei:Bezug-EPEX-Preis.png|600px|rahmenlos|links]]&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot; /&amp;gt;&lt;br /&gt;
Der SVG-Plot hat das &#039;&#039;fixedrange&#039;&#039; Attribut &amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;lt;small&amp;gt;{strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time-DAYSECONDS)).&amp;quot; &amp;quot;.strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+2*DAYSECONDS))}&amp;lt;/small&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Strompreis]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40738</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40738"/>
		<updated>2026-01-16T08:19:29Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* Anwendungsbeispiel */ SVG Attribut ergänzt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|Diese Wiki Seite ist noch in Bearbeitung! &amp;lt;br&amp;gt;Siehe auch {{Link2Forum|Topic=142165|LinkText=HTTPMOD: Aktueller Strompreis EEX Strombörse Leipzig abfragen}}}}&lt;br /&gt;
[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=142165|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Voraussetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Voraussetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Voraussetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente. &lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&amp;lt;div style=&amp;quot;max-height:150px; overflow:auto; padding:4px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries €/MWh prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
\&lt;br /&gt;
A reread every 15 min extracts the EPEX-price of the next quarter hour from the data lists in readings\&lt;br /&gt;
EPEX_prices and EPEX_timetags, using the code in reading02RecombineExpr. \&lt;br /&gt;
Within reading02RecombineExpr, &amp;quot;get $name EPEX_prices&amp;quot; is issued at the beginning of every second odd \&lt;br /&gt;
hour in order to keep EPEX_prices and EPEX_timetags up-to-date. \&lt;br /&gt;
Note that the reread itself does not perform an explicit request to api.energy-charts.info, except \&lt;br /&gt;
for a dummy request to localhost. This above strategy minimizes the number of external requests, \&lt;br /&gt;
in order to avoid occasional timeouts as much as possible.\&lt;br /&gt;
\&lt;br /&gt;
no_of_data, the length of the data lists in EPEX_prices and EPEX_timetags is 96 after midnight, and it \&lt;br /&gt;
changes to 192 in the afternoon when the dayahead prices are available.\&lt;br /&gt;
\&lt;br /&gt;
An event-on-change-reading for no_of_data can be used by a FileLog device to save the new data.&lt;br /&gt;
&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
if ($h % 2 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
    &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Code für device &#039;&#039;FileLog_EPEX_Boersenpreise&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define FileLog_EPEX_Boersenpreise FileLog ./log/EPEX_Boersenpreise.log EPEX_Boersenpreise:no_of_data:.192.*&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise outputFormat {my @prices=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;my @times=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;return join &amp;quot;&amp;quot;, map {strftime(&amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;, localtime($times[$_])).&amp;quot; &amp;quot;.&amp;quot;EPEX_Boersenpreise&amp;quot;.&amp;quot; EPEX_price $prices[$_]\n&amp;quot;} $#times - 95 .. $#times}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit einem at-device kann man die Länge der Daten im Log-File begrenzen, in diesem Beispiel auf drei Tage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define trim_EPEX_Logfile at *00:02:12 {`tail -n 288 ./log/EPEX_Boersenpreise.log &amp;gt; ./log/file.tmp &amp;amp;&amp;amp; mv ./log/file.tmp ./log/EPEX_Boersenpreise.log`;;fhem(&amp;quot;set FileLog_EPEX_Boersenpreise reopen&amp;quot;)}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Anwendungsbeispiel===&lt;br /&gt;
Im Beispiel sind der viertelstündliche Strombezug (falls mitgeloggt) und der viertelstündliche EPEX-Preis am 11.1.2026 (vormittags) dargestellt:&lt;br /&gt;
[[Datei:Bezug-EPEX-Preis.png|600px|rahmenlos|links]]&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot; /&amp;gt;&lt;br /&gt;
Der SVG-Plot hat das &#039;&#039;fixedrange&#039;&#039; Attribut &amp;lt;code&amp;gt;&amp;lt;small&amp;gt;{strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time-DAYSECONDS)).&amp;quot; &amp;quot;.strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+2*DAYSECONDS))}&amp;lt;/small&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Strompreis]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40731</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40731"/>
		<updated>2026-01-13T12:31:52Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* EPEX_Spot  RAW Device */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|Diese Wiki Seite ist noch in Bearbeitung! &amp;lt;br&amp;gt;Siehe auch {{Link2Forum|Topic=142165|LinkText=HTTPMOD: Aktueller Strompreis EEX Strombörse Leipzig abfragen}}}}&lt;br /&gt;
[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=142165|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Voraussetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Voraussetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Voraussetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente. &lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&amp;lt;div style=&amp;quot;max-height:150px; overflow:auto; padding:4px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries €/MWh prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
\&lt;br /&gt;
A reread every 15 min extracts the EPEX-price of the next quarter hour from the data lists in readings\&lt;br /&gt;
EPEX_prices and EPEX_timetags, using the code in reading02RecombineExpr. \&lt;br /&gt;
Within reading02RecombineExpr, &amp;quot;get $name EPEX_prices&amp;quot; is issued at the beginning of every second odd \&lt;br /&gt;
hour in order to keep EPEX_prices and EPEX_timetags up-to-date. \&lt;br /&gt;
Note that the reread itself does not perform an explicit request to api.energy-charts.info, except \&lt;br /&gt;
for a dummy request to localhost. This above strategy minimizes the number of external requests, \&lt;br /&gt;
in order to avoid occasional timeouts as much as possible.\&lt;br /&gt;
\&lt;br /&gt;
no_of_data, the length of the data lists in EPEX_prices and EPEX_timetags is 96 after midnight, and it \&lt;br /&gt;
changes to 192 in the afternoon when the dayahead prices are available.\&lt;br /&gt;
\&lt;br /&gt;
An event-on-change-reading for no_of_data can be used by a FileLog device to save the new data.&lt;br /&gt;
&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
if ($h % 2 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
    &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Code für device &#039;&#039;FileLog_EPEX_Boersenpreise&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define FileLog_EPEX_Boersenpreise FileLog ./log/EPEX_Boersenpreise.log EPEX_Boersenpreise:no_of_data:.192.*&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise outputFormat {my @prices=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;my @times=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;return join &amp;quot;&amp;quot;, map {strftime(&amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;, localtime($times[$_])).&amp;quot; &amp;quot;.&amp;quot;EPEX_Boersenpreise&amp;quot;.&amp;quot; EPEX_price $prices[$_]\n&amp;quot;} $#times - 95 .. $#times}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit einem at-device kann man die Länge der Daten im Log-File begrenzen, in diesem Beispiel auf drei Tage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define trim_EPEX_Logfile at *00:02:12 {`tail -n 288 ./log/EPEX_Boersenpreise.log &amp;gt; ./log/file.tmp &amp;amp;&amp;amp; mv ./log/file.tmp ./log/EPEX_Boersenpreise.log`;;fhem(&amp;quot;set FileLog_EPEX_Boersenpreise reopen&amp;quot;)}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Anwendungsbeispiel===&lt;br /&gt;
Im Beispiel sind der viertelstündliche Strombezug (falls mitgeloggt) und der viertelstündliche EPEX-Preis am 11.1.2026 (vormittags) dargestellt:&lt;br /&gt;
[[Datei:Bezug-EPEX-Preis.png|600px|rahmenlos|links]]&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Strompreis]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40718</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40718"/>
		<updated>2026-01-12T11:08:29Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* Anwendungsbeispiel */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|Diese Wiki Seite ist noch in Bearbeitung! &amp;lt;br&amp;gt;Siehe auch {{Link2Forum|Topic=142165|LinkText=HTTPMOD: Aktueller Strompreis EEX Strombörse Leipzig abfragen}}}}&lt;br /&gt;
[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=142165|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente. &lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&amp;lt;div style=&amp;quot;max-height:150px; overflow:auto; padding:4px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries €/MWh prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
\&lt;br /&gt;
A reread every 15 min extracts the EPEX-price of the next quarter hour from the data lists in readings\&lt;br /&gt;
EPEX_prices and EPEX_timetags, using the code in reading02RecombineExpr. \&lt;br /&gt;
Within reading02RecombineExpr, &amp;quot;get $name EPEX_prices&amp;quot; is issued at the beginning of every second odd \&lt;br /&gt;
hour in order to keep EPEX_prices and EPEX_timetags up-to-date. \&lt;br /&gt;
Note that the reread itself does not perform an explicit request to api.energy-charts.info, except \&lt;br /&gt;
for a dummy request to localhost. This above strategy minimizes the number of external requests, \&lt;br /&gt;
in order to avoid occasional timeouts as much as possible.\&lt;br /&gt;
\&lt;br /&gt;
no_of_data, the length of the data lists in EPEX_prices and EPEX_timetags is 96 after midnight, and it \&lt;br /&gt;
changes to 192 in the afternoon when the dayahead prices are available.\&lt;br /&gt;
\&lt;br /&gt;
An event-on-change-reading for no_of_data can be used by a FileLog device to save the new data.&lt;br /&gt;
&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
if ($h % 2 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Code für device &#039;&#039;FileLog_EPEX_Boersenpreise&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define FileLog_EPEX_Boersenpreise FileLog ./log/EPEX_Boersenpreise.log EPEX_Boersenpreise:no_of_data:.192.*&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise outputFormat {my @prices=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;my @times=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;return join &amp;quot;&amp;quot;, map {strftime(&amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;, localtime($times[$_])).&amp;quot; &amp;quot;.&amp;quot;EPEX_Boersenpreise&amp;quot;.&amp;quot; EPEX_price $prices[$_]\n&amp;quot;} $#times - 95 .. $#times}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit einem at-device kann man die Länge der Daten im Log-File begrenzen, in diesem Beispiel auf drei Tage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define trim_EPEX_Logfile at *00:02:12 {`tail -n 288 ./log/EPEX_Boersenpreise.log &amp;gt; ./log/file.tmp &amp;amp;&amp;amp; mv ./log/file.tmp ./log/EPEX_Boersenpreise.log`;;fhem(&amp;quot;set FileLog_EPEX_Boersenpreise reopen&amp;quot;)}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Anwendungsbeispiel===&lt;br /&gt;
Im Beispiel sind der viertelstündliche Strombezug (falls mitgeloggt) und der viertelstündliche EPEX-Preis am 11.1.2026 (vormittags) dargestellt:&lt;br /&gt;
[[Datei:Bezug-EPEX-Preis.png|600px|rahmenlos|links]]&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Strompreis]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40717</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40717"/>
		<updated>2026-01-12T11:08:08Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* Anwendungsbeispiel */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|Diese Wiki Seite ist noch in Bearbeitung! &amp;lt;br&amp;gt;Siehe auch {{Link2Forum|Topic=142165|LinkText=HTTPMOD: Aktueller Strompreis EEX Strombörse Leipzig abfragen}}}}&lt;br /&gt;
[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=142165|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente. &lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&amp;lt;div style=&amp;quot;max-height:150px; overflow:auto; padding:4px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries €/MWh prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
\&lt;br /&gt;
A reread every 15 min extracts the EPEX-price of the next quarter hour from the data lists in readings\&lt;br /&gt;
EPEX_prices and EPEX_timetags, using the code in reading02RecombineExpr. \&lt;br /&gt;
Within reading02RecombineExpr, &amp;quot;get $name EPEX_prices&amp;quot; is issued at the beginning of every second odd \&lt;br /&gt;
hour in order to keep EPEX_prices and EPEX_timetags up-to-date. \&lt;br /&gt;
Note that the reread itself does not perform an explicit request to api.energy-charts.info, except \&lt;br /&gt;
for a dummy request to localhost. This above strategy minimizes the number of external requests, \&lt;br /&gt;
in order to avoid occasional timeouts as much as possible.\&lt;br /&gt;
\&lt;br /&gt;
no_of_data, the length of the data lists in EPEX_prices and EPEX_timetags is 96 after midnight, and it \&lt;br /&gt;
changes to 192 in the afternoon when the dayahead prices are available.\&lt;br /&gt;
\&lt;br /&gt;
An event-on-change-reading for no_of_data can be used by a FileLog device to save the new data.&lt;br /&gt;
&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
if ($h % 2 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Code für device &#039;&#039;FileLog_EPEX_Boersenpreise&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define FileLog_EPEX_Boersenpreise FileLog ./log/EPEX_Boersenpreise.log EPEX_Boersenpreise:no_of_data:.192.*&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise outputFormat {my @prices=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;my @times=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;return join &amp;quot;&amp;quot;, map {strftime(&amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;, localtime($times[$_])).&amp;quot; &amp;quot;.&amp;quot;EPEX_Boersenpreise&amp;quot;.&amp;quot; EPEX_price $prices[$_]\n&amp;quot;} $#times - 95 .. $#times}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit einem at-device kann man die Länge der Daten im Log-File begrenzen, in diesem Beispiel auf drei Tage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define trim_EPEX_Logfile at *00:02:12 {`tail -n 288 ./log/EPEX_Boersenpreise.log &amp;gt; ./log/file.tmp &amp;amp;&amp;amp; mv ./log/file.tmp ./log/EPEX_Boersenpreise.log`;;fhem(&amp;quot;set FileLog_EPEX_Boersenpreise reopen&amp;quot;)}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Anwendungsbeispiel===&lt;br /&gt;
Im Beispiel sind der viertelstündliche Strombezug (falls mitgeloggt) und der viertelstündlichen EPEX-Preis am 11.1.2026 (vormittags) dargestellt:&lt;br /&gt;
[[Datei:Bezug-EPEX-Preis.png|600px|rahmenlos|links]]&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Strompreis]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40716</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40716"/>
		<updated>2026-01-12T11:07:28Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* EPEX_Spot  RAW Device */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|Diese Wiki Seite ist noch in Bearbeitung! &amp;lt;br&amp;gt;Siehe auch {{Link2Forum|Topic=142165|LinkText=HTTPMOD: Aktueller Strompreis EEX Strombörse Leipzig abfragen}}}}&lt;br /&gt;
[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=142165|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente. &lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&amp;lt;div style=&amp;quot;max-height:150px; overflow:auto; padding:4px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries €/MWh prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
\&lt;br /&gt;
A reread every 15 min extracts the EPEX-price of the next quarter hour from the data lists in readings\&lt;br /&gt;
EPEX_prices and EPEX_timetags, using the code in reading02RecombineExpr. \&lt;br /&gt;
Within reading02RecombineExpr, &amp;quot;get $name EPEX_prices&amp;quot; is issued at the beginning of every second odd \&lt;br /&gt;
hour in order to keep EPEX_prices and EPEX_timetags up-to-date. \&lt;br /&gt;
Note that the reread itself does not perform an explicit request to api.energy-charts.info, except \&lt;br /&gt;
for a dummy request to localhost. This above strategy minimizes the number of external requests, \&lt;br /&gt;
in order to avoid occasional timeouts as much as possible.\&lt;br /&gt;
\&lt;br /&gt;
no_of_data, the length of the data lists in EPEX_prices and EPEX_timetags is 96 after midnight, and it \&lt;br /&gt;
changes to 192 in the afternoon when the dayahead prices are available.\&lt;br /&gt;
\&lt;br /&gt;
An event-on-change-reading for no_of_data can be used by a FileLog device to save the new data.&lt;br /&gt;
&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
if ($h % 2 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Code für device &#039;&#039;FileLog_EPEX_Boersenpreise&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define FileLog_EPEX_Boersenpreise FileLog ./log/EPEX_Boersenpreise.log EPEX_Boersenpreise:no_of_data:.192.*&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise outputFormat {my @prices=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;my @times=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;return join &amp;quot;&amp;quot;, map {strftime(&amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;, localtime($times[$_])).&amp;quot; &amp;quot;.&amp;quot;EPEX_Boersenpreise&amp;quot;.&amp;quot; EPEX_price $prices[$_]\n&amp;quot;} $#times - 95 .. $#times}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit einem at-device kann man die Länge der Daten im Log-File begrenzen, in diesem Beispiel auf drei Tage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define trim_EPEX_Logfile at *00:02:12 {`tail -n 288 ./log/EPEX_Boersenpreise.log &amp;gt; ./log/file.tmp &amp;amp;&amp;amp; mv ./log/file.tmp ./log/EPEX_Boersenpreise.log`;;fhem(&amp;quot;set FileLog_EPEX_Boersenpreise reopen&amp;quot;)}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Anwendungsbeispiel===&lt;br /&gt;
Im Beispiel sind der viertelstündliche Strombezug und der viertelstündlichen EPEX-Preis am 11.1.2026 (vormittags) dargestellt:&lt;br /&gt;
[[Datei:Bezug-EPEX-Preis.png|600px|rahmenlos|links]]&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Strompreis]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40715</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40715"/>
		<updated>2026-01-12T11:04:26Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* EPEX_Spot  RAW Device */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|Diese Wiki Seite ist noch in Bearbeitung! &amp;lt;br&amp;gt;Siehe auch {{Link2Forum|Topic=142165|LinkText=HTTPMOD: Aktueller Strompreis EEX Strombörse Leipzig abfragen}}}}&lt;br /&gt;
[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=142165|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente.&amp;lt;br&amp;gt;&lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
sadgas&lt;br /&gt;
srg&lt;br /&gt;
SERG&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&lt;br /&gt;
&amp;lt;div style=&amp;quot;max-height:150px; overflow:auto; padding:4px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries €/MWh prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
\&lt;br /&gt;
A reread every 15 min extracts the EPEX-price of the next quarter hour from the data lists in readings\&lt;br /&gt;
EPEX_prices and EPEX_timetags, using the code in reading02RecombineExpr. \&lt;br /&gt;
Within reading02RecombineExpr, &amp;quot;get $name EPEX_prices&amp;quot; is issued at the beginning of every second odd \&lt;br /&gt;
hour in order to keep EPEX_prices and EPEX_timetags up-to-date. \&lt;br /&gt;
Note that the reread itself does not perform an explicit request to api.energy-charts.info, except \&lt;br /&gt;
for a dummy request to localhost. This above strategy minimizes the number of external requests, \&lt;br /&gt;
in order to avoid occasional timeouts as much as possible.\&lt;br /&gt;
\&lt;br /&gt;
no_of_data, the length of the data lists in EPEX_prices and EPEX_timetags is 96 after midnight, and it \&lt;br /&gt;
changes to 192 in the afternoon when the dayahead prices are available.\&lt;br /&gt;
\&lt;br /&gt;
An event-on-change-reading for no_of_data can be used by a FileLog device to save the new data.&lt;br /&gt;
&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
if ($h % 2 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Code für device &#039;&#039;FileLog_EPEX_Boersenpreise&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define FileLog_EPEX_Boersenpreise FileLog ./log/EPEX_Boersenpreise.log EPEX_Boersenpreise:no_of_data:.192.*&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise outputFormat {my @prices=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;my @times=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;return join &amp;quot;&amp;quot;, map {strftime(&amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;, localtime($times[$_])).&amp;quot; &amp;quot;.&amp;quot;EPEX_Boersenpreise&amp;quot;.&amp;quot; EPEX_price $prices[$_]\n&amp;quot;} $#times - 95 .. $#times}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit einem at-device kann man die Länge der Daten im Log-File begrenzen, in diesem Beispiel auf drei Tage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define trim_EPEX_Logfile at *00:02:12 {`tail -n 288 ./log/EPEX_Boersenpreise.log &amp;gt; ./log/file.tmp &amp;amp;&amp;amp; mv ./log/file.tmp ./log/EPEX_Boersenpreise.log`;;fhem(&amp;quot;set FileLog_EPEX_Boersenpreise reopen&amp;quot;)}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Anwendungsbeispiel===&lt;br /&gt;
Im Beispiel sind der viertelstündliche Strombezug und der viertelstündlichen EPEX-Preis am 11.1.2026 (vormittags) dargestellt:&lt;br /&gt;
[[Datei:Bezug-EPEX-Preis.png|600px|rahmenlos|links]]&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Strompreis]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40714</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40714"/>
		<updated>2026-01-12T11:01:54Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* EPEX_Spot  RAW Device */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|Diese Wiki Seite ist noch in Bearbeitung! &amp;lt;br&amp;gt;Siehe auch {{Link2Forum|Topic=142165|LinkText=HTTPMOD: Aktueller Strompreis EEX Strombörse Leipzig abfragen}}}}&lt;br /&gt;
[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=142165|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente.&amp;lt;br&amp;gt;&lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&lt;br /&gt;
&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&lt;br /&gt;
&amp;lt;div style=&amp;quot;max-height:150px; overflow:auto; padding:4px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries €/MWh prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
\&lt;br /&gt;
A reread every 15 min extracts the EPEX-price of the next quarter hour from the data lists in readings\&lt;br /&gt;
EPEX_prices and EPEX_timetags, using the code in reading02RecombineExpr. \&lt;br /&gt;
Within reading02RecombineExpr, &amp;quot;get $name EPEX_prices&amp;quot; is issued at the beginning of every second odd \&lt;br /&gt;
hour in order to keep EPEX_prices and EPEX_timetags up-to-date. \&lt;br /&gt;
Note that the reread itself does not perform an explicit request to api.energy-charts.info, except \&lt;br /&gt;
for a dummy request to localhost. This above strategy minimizes the number of external requests, \&lt;br /&gt;
in order to avoid occasional timeouts as much as possible.\&lt;br /&gt;
\&lt;br /&gt;
no_of_data, the length of the data lists in EPEX_prices and EPEX_timetags is 96 after midnight, and it \&lt;br /&gt;
changes to 192 in the afternoon when the dayahead prices are available.\&lt;br /&gt;
\&lt;br /&gt;
An event-on-change-reading for no_of_data can be used by a FileLog device to save the new data.&lt;br /&gt;
&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
if ($h % 2 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Code für device &#039;&#039;FileLog_EPEX_Boersenpreise&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define FileLog_EPEX_Boersenpreise FileLog ./log/EPEX_Boersenpreise.log EPEX_Boersenpreise:no_of_data:.192.*&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise outputFormat {my @prices=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;my @times=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;return join &amp;quot;&amp;quot;, map {strftime(&amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;, localtime($times[$_])).&amp;quot; &amp;quot;.&amp;quot;EPEX_Boersenpreise&amp;quot;.&amp;quot; EPEX_price $prices[$_]\n&amp;quot;} $#times - 95 .. $#times}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit einem at-device kann man die Länge der Daten im Log-File begrenzen, in diesem Beispiel auf drei Tage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define trim_EPEX_Logfile at *00:02:12 {`tail -n 288 ./log/EPEX_Boersenpreise.log &amp;gt; ./log/file.tmp &amp;amp;&amp;amp; mv ./log/file.tmp ./log/EPEX_Boersenpreise.log`;;fhem(&amp;quot;set FileLog_EPEX_Boersenpreise reopen&amp;quot;)}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Anwendungsbeispiel===&lt;br /&gt;
Im Beispiel sind der viertelstündliche Strombezug und der viertelstündlichen EPEX-Preis am 11.1.2026 (vormittags) dargestellt:&lt;br /&gt;
[[Datei:Bezug-EPEX-Preis.png|600px|rahmenlos|links]]&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Strompreis]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40713</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40713"/>
		<updated>2026-01-12T10:38:30Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* EPEX_Spot  RAW Device */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|Diese Wiki Seite ist noch in Bearbeitung! &amp;lt;br&amp;gt;Siehe auch {{Link2Forum|Topic=142165|LinkText=HTTPMOD: Aktueller Strompreis EEX Strombörse Leipzig abfragen}}}}&lt;br /&gt;
[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=142165|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente.&amp;lt;br&amp;gt;&lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&lt;br /&gt;
&amp;lt;div style=&amp;quot;max-height: 200px; overflow: auto; border: 1px solid #ccc;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:left;&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039; &lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries €/MWh prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
\&lt;br /&gt;
A reread every 15 min extracts the EPEX-price of the next quarter hour from the data lists in readings\&lt;br /&gt;
EPEX_prices and EPEX_timetags, using the code in reading02RecombineExpr. \&lt;br /&gt;
Within reading02RecombineExpr, &amp;quot;get $name EPEX_prices&amp;quot; is issued at the beginning of every second odd \&lt;br /&gt;
hour in order to keep EPEX_prices and EPEX_timetags up-to-date. \&lt;br /&gt;
Note that the reread itself does not perform an explicit request to api.energy-charts.info, except \&lt;br /&gt;
for a dummy request to localhost. This above strategy minimizes the number of external requests, \&lt;br /&gt;
in order to avoid occasional timeouts as much as possible.\&lt;br /&gt;
\&lt;br /&gt;
no_of_data, the length of the data lists in EPEX_prices and EPEX_timetags is 96 after midnight, and it \&lt;br /&gt;
changes to 192 in the afternoon when the dayahead prices are available.\&lt;br /&gt;
\&lt;br /&gt;
An event-on-change-reading for no_of_data can be used by a FileLog device to save the new data.&lt;br /&gt;
&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
if ($h % 2 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;FileLog_EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define FileLog_EPEX_Boersenpreise FileLog ./log/EPEX_Boersenpreise.log EPEX_Boersenpreise:no_of_data:.192.*&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise outputFormat {my @prices=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;my @times=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;return join &amp;quot;&amp;quot;, map {strftime(&amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;, localtime($times[$_])).&amp;quot; &amp;quot;.&amp;quot;EPEX_Boersenpreise&amp;quot;.&amp;quot; EPEX_price $prices[$_]\n&amp;quot;} $#times - 95 .. $#times}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Mit einem at-device kann man die Länge der Daten im Log-File begrenzen, in diesem Beispiel auf drei Tage:&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define trim_EPEX_Logfile at *00:02:12 {`tail -n 288 ./log/EPEX_Boersenpreise.log &amp;gt; ./log/file.tmp &amp;amp;&amp;amp; mv ./log/file.tmp ./log/EPEX_Boersenpreise.log`;;fhem(&amp;quot;set FileLog_EPEX_Boersenpreise reopen&amp;quot;)}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Anwendungsbeispiel===&lt;br /&gt;
Im Beispiel sind der viertelstündliche Strombezug und der viertelstündlichen EPEX-Preis am 11.1.2026 (vormittags) dargestellt:&lt;br /&gt;
[[Datei:Bezug-EPEX-Preis.png|600px|rahmenlos|links]]&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Strompreis]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40712</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40712"/>
		<updated>2026-01-12T10:37:57Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: Änderung 40710 von Alkazaa (Diskussion) rückgängig gemacht.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|Diese Wiki Seite ist noch in Bearbeitung! &amp;lt;br&amp;gt;Siehe auch {{Link2Forum|Topic=142165|LinkText=HTTPMOD: Aktueller Strompreis EEX Strombörse Leipzig abfragen}}}}&lt;br /&gt;
[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=142165|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente.&amp;lt;br&amp;gt;&lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&lt;br /&gt;
&amp;lt;div style=&amp;quot;max-height: 200px; overflow: auto; border: 1px solid #ccc;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:left;&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039; [[#|(kopieren)]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries €/MWh prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
\&lt;br /&gt;
A reread every 15 min extracts the EPEX-price of the next quarter hour from the data lists in readings\&lt;br /&gt;
EPEX_prices and EPEX_timetags, using the code in reading02RecombineExpr. \&lt;br /&gt;
Within reading02RecombineExpr, &amp;quot;get $name EPEX_prices&amp;quot; is issued at the beginning of every second odd \&lt;br /&gt;
hour in order to keep EPEX_prices and EPEX_timetags up-to-date. \&lt;br /&gt;
Note that the reread itself does not perform an explicit request to api.energy-charts.info, except \&lt;br /&gt;
for a dummy request to localhost. This above strategy minimizes the number of external requests, \&lt;br /&gt;
in order to avoid occasional timeouts as much as possible.\&lt;br /&gt;
\&lt;br /&gt;
no_of_data, the length of the data lists in EPEX_prices and EPEX_timetags is 96 after midnight, and it \&lt;br /&gt;
changes to 192 in the afternoon when the dayahead prices are available.\&lt;br /&gt;
\&lt;br /&gt;
An event-on-change-reading for no_of_data can be used by a FileLog device to save the new data.&lt;br /&gt;
&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
if ($h % 2 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;FileLog_EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define FileLog_EPEX_Boersenpreise FileLog ./log/EPEX_Boersenpreise.log EPEX_Boersenpreise:no_of_data:.192.*&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise outputFormat {my @prices=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;my @times=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;return join &amp;quot;&amp;quot;, map {strftime(&amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;, localtime($times[$_])).&amp;quot; &amp;quot;.&amp;quot;EPEX_Boersenpreise&amp;quot;.&amp;quot; EPEX_price $prices[$_]\n&amp;quot;} $#times - 95 .. $#times}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Mit einem at-device kann man die Länge der Daten im Log-File begrenzen, in diesem Beispiel auf drei Tage:&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define trim_EPEX_Logfile at *00:02:12 {`tail -n 288 ./log/EPEX_Boersenpreise.log &amp;gt; ./log/file.tmp &amp;amp;&amp;amp; mv ./log/file.tmp ./log/EPEX_Boersenpreise.log`;;fhem(&amp;quot;set FileLog_EPEX_Boersenpreise reopen&amp;quot;)}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Anwendungsbeispiel===&lt;br /&gt;
Im Beispiel sind der viertelstündliche Strombezug und der viertelstündlichen EPEX-Preis am 11.1.2026 (vormittags) dargestellt:&lt;br /&gt;
[[Datei:Bezug-EPEX-Preis.png|600px|rahmenlos|links]]&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Strompreis]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40711</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40711"/>
		<updated>2026-01-12T10:33:42Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* EPEX_Spot  RAW Device */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|Diese Wiki Seite ist noch in Bearbeitung! &amp;lt;br&amp;gt;Siehe auch {{Link2Forum|Topic=142165|LinkText=HTTPMOD: Aktueller Strompreis EEX Strombörse Leipzig abfragen}}}}&lt;br /&gt;
[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=142165|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente.&amp;lt;br&amp;gt;&lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&lt;br /&gt;
&amp;lt;div style=&amp;quot;max-height: 200px; overflow: auto; border: 1px solid #ccc;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:left;&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries €/MWh prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
\&lt;br /&gt;
A reread every 15 min extracts the EPEX-price of the next quarter hour from the data lists in readings\&lt;br /&gt;
EPEX_prices and EPEX_timetags, using the code in reading02RecombineExpr. \&lt;br /&gt;
Within reading02RecombineExpr, &amp;quot;get $name EPEX_prices&amp;quot; is issued at the beginning of every second odd \&lt;br /&gt;
hour in order to keep EPEX_prices and EPEX_timetags up-to-date. \&lt;br /&gt;
Note that the reread itself does not perform an explicit request to api.energy-charts.info, except \&lt;br /&gt;
for a dummy request to localhost. This above strategy minimizes the number of external requests, \&lt;br /&gt;
in order to avoid occasional timeouts as much as possible.\&lt;br /&gt;
\&lt;br /&gt;
no_of_data, the length of the data lists in EPEX_prices and EPEX_timetags is 96 after midnight, and it \&lt;br /&gt;
changes to 192 in the afternoon when the dayahead prices are available.\&lt;br /&gt;
\&lt;br /&gt;
An event-on-change-reading for no_of_data can be used by a FileLog device to save the new data.&lt;br /&gt;
&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
if ($h % 2 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;FileLog_EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define FileLog_EPEX_Boersenpreise FileLog ./log/EPEX_Boersenpreise.log EPEX_Boersenpreise:no_of_data:.192.*&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise outputFormat {my @prices=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;my @times=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;return join &amp;quot;&amp;quot;, map {strftime(&amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;, localtime($times[$_])).&amp;quot; &amp;quot;.&amp;quot;EPEX_Boersenpreise&amp;quot;.&amp;quot; EPEX_price $prices[$_]\n&amp;quot;} $#times - 95 .. $#times}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Mit einem at-device kann man die Länge der Daten im Log-File begrenzen, in diesem Beispiel auf drei Tage:&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define trim_EPEX_Logfile at *00:02:12 {`tail -n 288 ./log/EPEX_Boersenpreise.log &amp;gt; ./log/file.tmp &amp;amp;&amp;amp; mv ./log/file.tmp ./log/EPEX_Boersenpreise.log`;;fhem(&amp;quot;set FileLog_EPEX_Boersenpreise reopen&amp;quot;)}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Anwendungsbeispiel===&lt;br /&gt;
Im Beispiel sind der viertelstündliche Strombezug und der viertelstündlichen EPEX-Preis am 11.1.2026 (vormittags) dargestellt:&lt;br /&gt;
[[Datei:Bezug-EPEX-Preis.png|600px|rahmenlos|links]]&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Strompreis]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40710</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40710"/>
		<updated>2026-01-12T10:29:54Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* EPEX_Spot  RAW Device */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|Diese Wiki Seite ist noch in Bearbeitung! &amp;lt;br&amp;gt;Siehe auch {{Link2Forum|Topic=142165|LinkText=HTTPMOD: Aktueller Strompreis EEX Strombörse Leipzig abfragen}}}}&lt;br /&gt;
[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=142165|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente.&amp;lt;br&amp;gt;&lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&lt;br /&gt;
&amp;lt;div style=&amp;quot;max-height: 200px; overflow: auto; border: 1px solid #ccc;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:left;&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries €/MWh prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
\&lt;br /&gt;
A reread every 15 min extracts the EPEX-price of the next quarter hour from the data lists in readings\&lt;br /&gt;
EPEX_prices and EPEX_timetags, using the code in reading02RecombineExpr. \&lt;br /&gt;
Within reading02RecombineExpr, &amp;quot;get $name EPEX_prices&amp;quot; is issued at the beginning of every second odd \&lt;br /&gt;
hour in order to keep EPEX_prices and EPEX_timetags up-to-date. \&lt;br /&gt;
Note that the reread itself does not perform an explicit request to api.energy-charts.info, except \&lt;br /&gt;
for a dummy request to localhost. This above strategy minimizes the number of external requests, \&lt;br /&gt;
in order to avoid occasional timeouts as much as possible.\&lt;br /&gt;
\&lt;br /&gt;
no_of_data, the length of the data lists in EPEX_prices and EPEX_timetags is 96 after midnight, and it \&lt;br /&gt;
changes to 192 in the afternoon when the dayahead prices are available.\&lt;br /&gt;
\&lt;br /&gt;
An event-on-change-reading for no_of_data can be used by a FileLog device to save the new data.&lt;br /&gt;
&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
if ($h % 2 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;FileLog_EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define FileLog_EPEX_Boersenpreise FileLog ./log/EPEX_Boersenpreise.log EPEX_Boersenpreise:no_of_data:.192.*&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise outputFormat {my @prices=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;my @times=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;return join &amp;quot;&amp;quot;, map {strftime(&amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;, localtime($times[$_])).&amp;quot; &amp;quot;.&amp;quot;EPEX_Boersenpreise&amp;quot;.&amp;quot; EPEX_price $prices[$_]\n&amp;quot;} $#times - 95 .. $#times}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Mit einem at-device kann man die Länge der Daten im Log-File begrenzen, in diesem Beispiel auf drei Tage:&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define trim_EPEX_Logfile at *00:02:12 {`tail -n 288 ./log/EPEX_Boersenpreise.log &amp;gt; ./log/file.tmp &amp;amp;&amp;amp; mv ./log/file.tmp ./log/EPEX_Boersenpreise.log`;;fhem(&amp;quot;set FileLog_EPEX_Boersenpreise reopen&amp;quot;)}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Anwendungsbeispiel===&lt;br /&gt;
Im Beispiel sind der viertelstündliche Strombezug und der viertelstündlichen EPEX-Preis am 11.1.2026 (vormittags) dargestellt:&lt;br /&gt;
[[Datei:Bezug-EPEX-Preis.png|600px|rahmenlos|links]]&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Strompreis]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40709</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40709"/>
		<updated>2026-01-12T10:23:43Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* EPEX_Spot  RAW Device */  collapsible code Block durch scrollbaren Block ersetzt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|Diese Wiki Seite ist noch in Bearbeitung! &amp;lt;br&amp;gt;Siehe auch {{Link2Forum|Topic=142165|LinkText=HTTPMOD: Aktueller Strompreis EEX Strombörse Leipzig abfragen}}}}&lt;br /&gt;
[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=142165|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente.&amp;lt;br&amp;gt;&lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&lt;br /&gt;
&amp;lt;div style=&amp;quot;max-height: 200px; overflow: auto; border: 1px solid #ccc;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:left;&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039; [[#|(kopieren)]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries €/MWh prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
\&lt;br /&gt;
A reread every 15 min extracts the EPEX-price of the next quarter hour from the data lists in readings\&lt;br /&gt;
EPEX_prices and EPEX_timetags, using the code in reading02RecombineExpr. \&lt;br /&gt;
Within reading02RecombineExpr, &amp;quot;get $name EPEX_prices&amp;quot; is issued at the beginning of every second odd \&lt;br /&gt;
hour in order to keep EPEX_prices and EPEX_timetags up-to-date. \&lt;br /&gt;
Note that the reread itself does not perform an explicit request to api.energy-charts.info, except \&lt;br /&gt;
for a dummy request to localhost. This above strategy minimizes the number of external requests, \&lt;br /&gt;
in order to avoid occasional timeouts as much as possible.\&lt;br /&gt;
\&lt;br /&gt;
no_of_data, the length of the data lists in EPEX_prices and EPEX_timetags is 96 after midnight, and it \&lt;br /&gt;
changes to 192 in the afternoon when the dayahead prices are available.\&lt;br /&gt;
\&lt;br /&gt;
An event-on-change-reading for no_of_data can be used by a FileLog device to save the new data.&lt;br /&gt;
&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
if ($h % 2 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;FileLog_EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define FileLog_EPEX_Boersenpreise FileLog ./log/EPEX_Boersenpreise.log EPEX_Boersenpreise:no_of_data:.192.*&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise outputFormat {my @prices=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;my @times=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;return join &amp;quot;&amp;quot;, map {strftime(&amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;, localtime($times[$_])).&amp;quot; &amp;quot;.&amp;quot;EPEX_Boersenpreise&amp;quot;.&amp;quot; EPEX_price $prices[$_]\n&amp;quot;} $#times - 95 .. $#times}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Mit einem at-device kann man die Länge der Daten im Log-File begrenzen, in diesem Beispiel auf drei Tage:&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define trim_EPEX_Logfile at *00:02:12 {`tail -n 288 ./log/EPEX_Boersenpreise.log &amp;gt; ./log/file.tmp &amp;amp;&amp;amp; mv ./log/file.tmp ./log/EPEX_Boersenpreise.log`;;fhem(&amp;quot;set FileLog_EPEX_Boersenpreise reopen&amp;quot;)}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Anwendungsbeispiel===&lt;br /&gt;
Im Beispiel sind der viertelstündliche Strombezug und der viertelstündlichen EPEX-Preis am 11.1.2026 (vormittags) dargestellt:&lt;br /&gt;
[[Datei:Bezug-EPEX-Preis.png|600px|rahmenlos|links]]&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Strompreis]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40705</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40705"/>
		<updated>2026-01-12T05:36:00Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* Anwendungsbeispiel */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|Diese Wiki Seite ist noch in Bearbeitung! &amp;lt;br&amp;gt;Siehe auch {{Link2Forum|Topic=142165|LinkText=HTTPMOD: Aktueller Strompreis EEX Strombörse Leipzig abfragen}}}}&lt;br /&gt;
[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=142165|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente.&amp;lt;br&amp;gt;&lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries €/MWh prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
\&lt;br /&gt;
A reread every 15 min extracts the EPEX-price of the next quarter hour from the data lists in readings\&lt;br /&gt;
EPEX_prices and EPEX_timetags, using the code in reading02RecombineExpr. \&lt;br /&gt;
Within reading02RecombineExpr, &amp;quot;get $name EPEX_prices&amp;quot; is issued at the beginning of every second odd \&lt;br /&gt;
hour in order to keep EPEX_prices and EPEX_timetags up-to-date. \&lt;br /&gt;
Note that the reread itself does not perform an explicit request to api.energy-charts.info, except \&lt;br /&gt;
for a dummy request to localhost. This above strategy minimizes the number of external requests, \&lt;br /&gt;
in order to avoid occasional timeouts as much as possible.\&lt;br /&gt;
\&lt;br /&gt;
no_of_data, the length of the data lists in EPEX_prices and EPEX_timetags is 96 after midnight, and it \&lt;br /&gt;
changes to 192 in the afternoon when the dayahead prices are available.\&lt;br /&gt;
\&lt;br /&gt;
An event-on-change-reading for no_of_data can be used by a FileLog device to save the new data.&lt;br /&gt;
&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
if ($h % 2 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;FileLog_EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define FileLog_EPEX_Boersenpreise FileLog ./log/EPEX_Boersenpreise.log EPEX_Boersenpreise:no_of_data:.192.*&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise outputFormat {my @prices=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;my @times=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;return join &amp;quot;&amp;quot;, map {strftime(&amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;, localtime($times[$_])).&amp;quot; &amp;quot;.&amp;quot;EPEX_Boersenpreise&amp;quot;.&amp;quot; EPEX_price $prices[$_]\n&amp;quot;} $#times - 95 .. $#times}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Mit einem at-device kann man die Länge der Daten im Log-File begrenzen, in diesem Beispiel auf drei Tage:&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define trim_EPEX_Logfile at *00:02:12 {`tail -n 288 ./log/EPEX_Boersenpreise.log &amp;gt; ./log/file.tmp &amp;amp;&amp;amp; mv ./log/file.tmp ./log/EPEX_Boersenpreise.log`;;fhem(&amp;quot;set FileLog_EPEX_Boersenpreise reopen&amp;quot;)}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Anwendungsbeispiel===&lt;br /&gt;
Im Beispiel sind der viertelstündliche Strombezug und der viertelstündlichen EPEX-Preis am 11.1.2026 (vormittags) dargestellt:&lt;br /&gt;
[[Datei:Bezug-EPEX-Preis.png|600px|rahmenlos|links]]&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot; /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Strompreis]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40704</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40704"/>
		<updated>2026-01-12T04:49:05Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: corrected duplicate link&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|Diese Wiki Seite ist noch in Bearbeitung! &amp;lt;br&amp;gt;Siehe auch {{Link2Forum|Topic=142165|LinkText=HTTPMOD: Aktueller Strompreis EEX Strombörse Leipzig abfragen}}}}&lt;br /&gt;
[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=142165|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente.&amp;lt;br&amp;gt;&lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries €/MWh prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
\&lt;br /&gt;
A reread every 15 min extracts the EPEX-price of the next quarter hour from the data lists in readings\&lt;br /&gt;
EPEX_prices and EPEX_timetags, using the code in reading02RecombineExpr. \&lt;br /&gt;
Within reading02RecombineExpr, &amp;quot;get $name EPEX_prices&amp;quot; is issued at the beginning of every second odd \&lt;br /&gt;
hour in order to keep EPEX_prices and EPEX_timetags up-to-date. \&lt;br /&gt;
Note that the reread itself does not perform an explicit request to api.energy-charts.info, except \&lt;br /&gt;
for a dummy request to localhost. This above strategy minimizes the number of external requests, \&lt;br /&gt;
in order to avoid occasional timeouts as much as possible.\&lt;br /&gt;
\&lt;br /&gt;
no_of_data, the length of the data lists in EPEX_prices and EPEX_timetags is 96 after midnight, and it \&lt;br /&gt;
changes to 192 in the afternoon when the dayahead prices are available.\&lt;br /&gt;
\&lt;br /&gt;
An event-on-change-reading for no_of_data can be used by a FileLog device to save the new data.&lt;br /&gt;
&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
if ($h % 2 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;FileLog_EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define FileLog_EPEX_Boersenpreise FileLog ./log/EPEX_Boersenpreise.log EPEX_Boersenpreise:no_of_data:.192.*&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise outputFormat {my @prices=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;my @times=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;return join &amp;quot;&amp;quot;, map {strftime(&amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;, localtime($times[$_])).&amp;quot; &amp;quot;.&amp;quot;EPEX_Boersenpreise&amp;quot;.&amp;quot; EPEX_price $prices[$_]\n&amp;quot;} $#times - 95 .. $#times}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Mit einem at-device kann man die Länge der Daten im Log-File begrenzen, in diesem Beispiel auf drei Tage:&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define trim_EPEX_Logfile at *00:02:12 {`tail -n 288 ./log/EPEX_Boersenpreise.log &amp;gt; ./log/file.tmp &amp;amp;&amp;amp; mv ./log/file.tmp ./log/EPEX_Boersenpreise.log`;;fhem(&amp;quot;set FileLog_EPEX_Boersenpreise reopen&amp;quot;)}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Anwendungsbeispiel===&lt;br /&gt;
Im Beispiel sind der viertelstündliche Strombezug und der viertelstündlichen EPEX-Preis am 11.1.2026 (vormittags) dargestellt:&lt;br /&gt;
[[Datei:Bezug-EPEX-Preis.png|600px|rahmenlos|links]]&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Strompreis]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40689</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40689"/>
		<updated>2026-01-11T11:13:13Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* EPEX_Spot  RAW Device */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|Diese Wiki Seite ist noch in Bearbeitung! &amp;lt;br&amp;gt;Siehe auch {{Link2Forum|Topic=142165|LinkText=HTTPMOD: Aktueller Strompreis EEX Strombörse Leipzig abfragen}}}}&lt;br /&gt;
[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente.&amp;lt;br&amp;gt;&lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries €/MWh prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
\&lt;br /&gt;
A reread every 15 min extracts the EPEX-price of the next quarter hour from the data lists in readings\&lt;br /&gt;
EPEX_prices and EPEX_timetags, using the code in reading02RecombineExpr. \&lt;br /&gt;
Within reading02RecombineExpr, &amp;quot;get $name EPEX_prices&amp;quot; is issued at the beginning of every second odd \&lt;br /&gt;
hour in order to keep EPEX_prices and EPEX_timetags up-to-date. \&lt;br /&gt;
Note that the reread itself does not perform an explicit request to api.energy-charts.info, except \&lt;br /&gt;
for a dummy request to localhost. This above strategy minimizes the number of external requests, \&lt;br /&gt;
in order to avoid occasional timeouts as much as possible.\&lt;br /&gt;
\&lt;br /&gt;
no_of_data, the length of the data lists in EPEX_prices and EPEX_timetags is 96 after midnight, and it \&lt;br /&gt;
changes to 192 in the afternoon when the dayahead prices are available.\&lt;br /&gt;
\&lt;br /&gt;
An event-on-change-reading for no_of_data can be used by a FileLog device to save the new data.&lt;br /&gt;
&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
if ($h % 2 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;FileLog_EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define FileLog_EPEX_Boersenpreise FileLog ./log/EPEX_Boersenpreise.log EPEX_Boersenpreise:no_of_data:.192.*&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise outputFormat {my @prices=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;my @times=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;return join &amp;quot;&amp;quot;, map {strftime(&amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;, localtime($times[$_])).&amp;quot; &amp;quot;.&amp;quot;EPEX_Boersenpreise&amp;quot;.&amp;quot; EPEX_price $prices[$_]\n&amp;quot;} $#times - 95 .. $#times}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Mit einem at-device kann man die Länge der Daten im Log-File begrenzen, in diesem Beispiel auf drei Tage:&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define trim_EPEX_Logfile at *00:02:12 {`tail -n 288 ./log/EPEX_Boersenpreise.log &amp;gt; ./log/file.tmp &amp;amp;&amp;amp; mv ./log/file.tmp ./log/EPEX_Boersenpreise.log`;;fhem(&amp;quot;set FileLog_EPEX_Boersenpreise reopen&amp;quot;)}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Anwendungsbeispiel===&lt;br /&gt;
Im Beispiel sind der viertelstündliche Strombezug und der viertelstündlichen EPEX-Preis am 11.1.2026 (vormittags) dargestellt:&lt;br /&gt;
[[Datei:Bezug-EPEX-Preis.png|600px|rahmenlos|links]]&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Strompreis]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40688</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40688"/>
		<updated>2026-01-11T11:12:05Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* EPEX_Spot  RAW Device */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|Diese Wiki Seite ist noch in Bearbeitung! &amp;lt;br&amp;gt;Siehe auch {{Link2Forum|Topic=142165|LinkText=HTTPMOD: Aktueller Strompreis EEX Strombörse Leipzig abfragen}}}}&lt;br /&gt;
[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente.&amp;lt;br&amp;gt;&lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries €/MWh prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
\&lt;br /&gt;
A reread every 15 min extracts the EPEX-price of the next quarter hour from the data lists in readings\&lt;br /&gt;
EPEX_prices and EPEX_timetags, using the code in reading02RecombineExpr. \&lt;br /&gt;
Within reading02RecombineExpr, &amp;quot;get $name EPEX_prices&amp;quot; is issued at the beginning of every second odd \&lt;br /&gt;
hour in order to keep EPEX_prices and EPEX_timetags up-to-date. \&lt;br /&gt;
Note that the reread itself does not perform an explicit request to api.energy-charts.info, except \&lt;br /&gt;
for a dummy request to localhost. This above strategy minimizes the number of external requests, \&lt;br /&gt;
in order to avoid occasional timeouts as much as possible.\&lt;br /&gt;
\&lt;br /&gt;
no_of_data, the length of the data lists in EPEX_prices and EPEX_timetags is 96 after midnight, and it \&lt;br /&gt;
changes to 192 in the afternoon when the dayahead prices are available.\&lt;br /&gt;
\&lt;br /&gt;
An event-on-change-reading for no_of_data can be used by a FileLog device to save the new data.&lt;br /&gt;
&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
if ($h % 2 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;FileLog_EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define FileLog_EPEX_Boersenpreise FileLog ./log/EPEX_Boersenpreise.log EPEX_Boersenpreise:no_of_data:.192.*&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise outputFormat {my @prices=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;my @times=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;return join &amp;quot;&amp;quot;, map {strftime(&amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;, localtime($times[$_])).&amp;quot; &amp;quot;.&amp;quot;EPEX_Boersenpreise&amp;quot;.&amp;quot; EPEX_price $prices[$_]\n&amp;quot;} $#times - 95 .. $#times}&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise room Xprimtl&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Mit einem at-device kann man die Länge der Daten im Log-File begrenzen, in diesem Beispiel auf drei Tage:&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define trim_EPEX_Logfile at *00:02:12 {`tail -n 288 ./log/EPEX_Boersenpreise.log &amp;gt; ./log/file.tmp &amp;amp;&amp;amp; mv ./log/file.tmp ./log/EPEX_Boersenpreise.log`;;fhem(&amp;quot;set FileLog_EPEX_Boersenpreise reopen&amp;quot;)}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Anwendungsbeispiel===&lt;br /&gt;
Im Beispiel sind der viertelstündliche Strombezug und der viertelstündlichen EPEX-Preis am 11.1.2026 (vormittags) dargestellt:&lt;br /&gt;
[[Datei:Bezug-EPEX-Preis.png|600px|rahmenlos|links]]&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Strompreis]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40687</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40687"/>
		<updated>2026-01-11T10:57:44Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* 3. EPEX_Spot */  added example SVG graphics&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|Diese Wiki Seite ist noch in Bearbeitung! &amp;lt;br&amp;gt;Siehe auch {{Link2Forum|Topic=142165|LinkText=HTTPMOD: Aktueller Strompreis EEX Strombörse Leipzig abfragen}}}}&lt;br /&gt;
[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente.&amp;lt;br&amp;gt;&lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries €/MWh prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
\&lt;br /&gt;
A reread every 15 min extracts the EPEX-price of the next quarter hour from the data lists in readings\&lt;br /&gt;
EPEX_prices and EPEX_timetags, using the code in reading02RecombineExpr. \&lt;br /&gt;
Within reading02RecombineExpr, &amp;quot;get $name EPEX_prices&amp;quot; is issued at the beginning of every second odd \&lt;br /&gt;
hour in order to keep EPEX_prices and EPEX_timetags up-to-date. \&lt;br /&gt;
Note that the reread itself does not perform an explicit request to api.energy-charts.info, except \&lt;br /&gt;
for a dummy request to localhost. This above strategy minimizes the number of external requests, \&lt;br /&gt;
in order to avoid occasional timeouts as much as possible.\&lt;br /&gt;
\&lt;br /&gt;
no_of_data, the length of the data lists in EPEX_prices and EPEX_timetags is 96 after midnight, and it \&lt;br /&gt;
changes to 192 in the afternoon when the dayahead prices are available.\&lt;br /&gt;
\&lt;br /&gt;
An event-on-change-reading for no_of_data can be used by a FileLog device to save the new data.&lt;br /&gt;
&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
if ($h % 2 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;FileLog_EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define FileLog_EPEX_Boersenpreise FileLog ./log/EPEX_Boersenpreise.log EPEX_Boersenpreise:no_of_data:.192.*&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise outputFormat {my @prices=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;my @times=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;return join &amp;quot;&amp;quot;, map {strftime(&amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;, localtime($times[$_])).&amp;quot; &amp;quot;.&amp;quot;EPEX_Boersenpreise&amp;quot;.&amp;quot; EPEX_price $prices[$_]\n&amp;quot;} $#times - 95 .. $#times}&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise room Xprimtl&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Mit einem at-device kann man die Länge der Daten im Log-File begrenzen, in diesem Beispiel auf drei Tage:&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define trim_EPEX_Logfile at *00:02:12 {`tail -n 288 ./log/EPEX_Boersenpreise.log &amp;gt; ./log/file.tmp &amp;amp;&amp;amp; mv ./log/file.tmp ./log/EPEX_Boersenpreise.log`;;fhem(&amp;quot;set FileLog_EPEX_Boersenpreise reopen&amp;quot;)}&lt;br /&gt;
attr trim_EPEX_Logfile room at_doif_notify&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
=== Anwendungsbeispiel===&lt;br /&gt;
Im Beispiel sind der viertelstündliche Strombezug und der viertelstündlichen EPEX-Preis am 11.1.2026 (vormittags) dargestellt:&lt;br /&gt;
[[Datei:Bezug-EPEX-Preis.png|600px|rahmenlos|links]]&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Strompreis]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Datei:Bezug-EPEX-Preis.png&amp;diff=40686</id>
		<title>Datei:Bezug-EPEX-Preis.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Datei:Bezug-EPEX-Preis.png&amp;diff=40686"/>
		<updated>2026-01-11T10:48:08Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: screenshot eines SVG&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Beschreibung ==&lt;br /&gt;
screenshot eines SVG&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40685</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40685"/>
		<updated>2026-01-11T10:40:40Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* EPEX_Spot  RAW Device */ added trim_EPEX_Logfile&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|Diese Wiki Seite ist noch in Bearbeitung! &amp;lt;br&amp;gt;Siehe auch {{Link2Forum|Topic=142165|LinkText=HTTPMOD: Aktueller Strompreis EEX Strombörse Leipzig abfragen}}}}&lt;br /&gt;
[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente.&amp;lt;br&amp;gt;&lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries €/MWh prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
\&lt;br /&gt;
A reread every 15 min extracts the EPEX-price of the next quarter hour from the data lists in readings\&lt;br /&gt;
EPEX_prices and EPEX_timetags, using the code in reading02RecombineExpr. \&lt;br /&gt;
Within reading02RecombineExpr, &amp;quot;get $name EPEX_prices&amp;quot; is issued at the beginning of every second odd \&lt;br /&gt;
hour in order to keep EPEX_prices and EPEX_timetags up-to-date. \&lt;br /&gt;
Note that the reread itself does not perform an explicit request to api.energy-charts.info, except \&lt;br /&gt;
for a dummy request to localhost. This above strategy minimizes the number of external requests, \&lt;br /&gt;
in order to avoid occasional timeouts as much as possible.\&lt;br /&gt;
\&lt;br /&gt;
no_of_data, the length of the data lists in EPEX_prices and EPEX_timetags is 96 after midnight, and it \&lt;br /&gt;
changes to 192 in the afternoon when the dayahead prices are available.\&lt;br /&gt;
\&lt;br /&gt;
An event-on-change-reading for no_of_data can be used by a FileLog device to save the new data.&lt;br /&gt;
&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
if ($h % 2 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;FileLog_EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define FileLog_EPEX_Boersenpreise FileLog ./log/EPEX_Boersenpreise.log EPEX_Boersenpreise:no_of_data:.192.*&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise outputFormat {my @prices=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;my @times=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;return join &amp;quot;&amp;quot;, map {strftime(&amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;, localtime($times[$_])).&amp;quot; &amp;quot;.&amp;quot;EPEX_Boersenpreise&amp;quot;.&amp;quot; EPEX_price $prices[$_]\n&amp;quot;} $#times - 95 .. $#times}&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise room Xprimtl&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Mit einem at-device kann man die Länge der Daten im Log-File begrenzen, in diesem Beispiel auf drei Tage:&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define trim_EPEX_Logfile at *00:02:12 {`tail -n 288 ./log/EPEX_Boersenpreise.log &amp;gt; ./log/file.tmp &amp;amp;&amp;amp; mv ./log/file.tmp ./log/EPEX_Boersenpreise.log`;;fhem(&amp;quot;set FileLog_EPEX_Boersenpreise reopen&amp;quot;)}&lt;br /&gt;
attr trim_EPEX_Logfile room at_doif_notify&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Strompreis]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40684</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40684"/>
		<updated>2026-01-11T10:34:56Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* EPEX_Spot  RAW Device */  added code for FileLog_EPEX_Boersenpreise&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|Diese Wiki Seite ist noch in Bearbeitung! &amp;lt;br&amp;gt;Siehe auch {{Link2Forum|Topic=142165|LinkText=HTTPMOD: Aktueller Strompreis EEX Strombörse Leipzig abfragen}}}}&lt;br /&gt;
[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente.&amp;lt;br&amp;gt;&lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries €/MWh prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
\&lt;br /&gt;
A reread every 15 min extracts the EPEX-price of the next quarter hour from the data lists in readings\&lt;br /&gt;
EPEX_prices and EPEX_timetags, using the code in reading02RecombineExpr. \&lt;br /&gt;
Within reading02RecombineExpr, &amp;quot;get $name EPEX_prices&amp;quot; is issued at the beginning of every second odd \&lt;br /&gt;
hour in order to keep EPEX_prices and EPEX_timetags up-to-date. \&lt;br /&gt;
Note that the reread itself does not perform an explicit request to api.energy-charts.info, except \&lt;br /&gt;
for a dummy request to localhost. This above strategy minimizes the number of external requests, \&lt;br /&gt;
in order to avoid occasional timeouts as much as possible.\&lt;br /&gt;
\&lt;br /&gt;
no_of_data, the length of the data lists in EPEX_prices and EPEX_timetags is 96 after midnight, and it \&lt;br /&gt;
changes to 192 in the afternoon when the dayahead prices are available.\&lt;br /&gt;
\&lt;br /&gt;
An event-on-change-reading for no_of_data can be used by a FileLog device to save the new data.&lt;br /&gt;
&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
if ($h % 2 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;FileLog_EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define FileLog_EPEX_Boersenpreise FileLog ./log/EPEX_Boersenpreise.log EPEX_Boersenpreise:no_of_data:.192.*&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise outputFormat {my @prices=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;my @times=split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;return join &amp;quot;&amp;quot;, map {strftime(&amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;, localtime($times[$_])).&amp;quot; &amp;quot;.&amp;quot;EPEX_Boersenpreise&amp;quot;.&amp;quot; EPEX_price $prices[$_]\n&amp;quot;} $#times - 95 .. $#times}&lt;br /&gt;
attr FileLog_EPEX_Boersenpreise room Xprimtl&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Strompreis]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40683</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40683"/>
		<updated>2026-01-11T10:29:29Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* EPEX_Spot  RAW Device */  extended comment in device definition&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Todo|Diese Wiki Seite ist noch in Bearbeitung! &amp;lt;br&amp;gt;Siehe auch {{Link2Forum|Topic=142165|LinkText=HTTPMOD: Aktueller Strompreis EEX Strombörse Leipzig abfragen}}}}&lt;br /&gt;
[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente.&amp;lt;br&amp;gt;&lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries €/MWh prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
\&lt;br /&gt;
A reread every 15 min extracts the EPEX-price of the next quarter hour from the data lists in readings\&lt;br /&gt;
EPEX_prices and EPEX_timetags, using the code in reading02RecombineExpr. \&lt;br /&gt;
Within reading02RecombineExpr, &amp;quot;get $name EPEX_prices&amp;quot; is issued at the beginning of every second odd \&lt;br /&gt;
hour in order to keep EPEX_prices and EPEX_timetags up-to-date. \&lt;br /&gt;
Note that the reread itself does not perform an explicit request to api.energy-charts.info, except \&lt;br /&gt;
for a dummy request to localhost. This above strategy minimizes the number of external requests, \&lt;br /&gt;
in order to avoid occasional timeouts as much as possible.\&lt;br /&gt;
\&lt;br /&gt;
no_of_data, the length of the data lists in EPEX_prices and EPEX_timetags is 96 after midnight, and it \&lt;br /&gt;
changes to 192 in the afternoon when the dayahead prices are available.\&lt;br /&gt;
\&lt;br /&gt;
An event-on-change-reading for no_of_data can be used by a FileLog device to save the new data.&lt;br /&gt;
&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
if ($h % 2 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Strompreis]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung&amp;diff=40490</id>
		<title>SolarForecast - Solare Prognose (PV Erzeugung) und Verbrauchersteuerung</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung&amp;diff=40490"/>
		<updated>2025-11-08T17:55:48Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* Einbindung eines Fronius Symo GEN24 10.0 Plus mit BYD Batterie ohne (virtuellen) Batterie-Wechselrichter */  Link auf PV-Batterie gesetzt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Baustelle}}&lt;br /&gt;
{{Hinweis|Die vorliegende Wiki-Seite befindet sich im Aufbau.}}&lt;br /&gt;
{{Infobox Modul&lt;br /&gt;
|ModPurpose=Solarprognose und Verbrauchersteuerung&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=76_SolarForecast&lt;br /&gt;
|ModFTopic=117864&lt;br /&gt;
|ModForumArea=Solaranlagen&lt;br /&gt;
|ModTechName=76_SolarForecast.pm&lt;br /&gt;
|ModOwner=DS_Starter ({{Link2FU|16933|Forum}}/[[Benutzer Diskussion:DS_Starter|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
[[SolarForecast - Solare Prognose (PV Erzeugung) und Verbrauchersteuerung|SolarForecast]] ist ein integratives Modul zur Gewinnung solarer Vorhersagedaten, deren Verarbeitung und grafischen Darstellung. Desweiteren bietet es die Möglichkeit, in FHEM definierte Verbraucher in einem SolarForecast-Device zu registrieren und eine PV Prognose basierte Steuerung der Verbraucher vom Modul übernehmen zu lassen.&lt;br /&gt;
&lt;br /&gt;
Das Modul ist insbesondere durch folgende Eigenschaften gekennzeichnet:&lt;br /&gt;
[[Datei:sfc1.png|right|thumb|300px|grafische Ansicht SolarForecast]]&lt;br /&gt;
&lt;br /&gt;
::* zur Gewinnung solarer Prognosewerte können die SolCast API, OpenMeteo API, Forecast.Solar API, Victron VRM Portal API oder alternativ DWD Stahlungswerte-Stationen verwendet werden&lt;br /&gt;
&lt;br /&gt;
::* optionale KI Unterstützung der PV-Prognose bei Verwendung von geeigneten Solar-API&#039;s&lt;br /&gt;
&lt;br /&gt;
::* es wird sowohl die Erzeugungsprognose als auch eine Verbrauchsprognose sowie SoC Prognose (bei Batterie-Integration) erstellt&lt;br /&gt;
&lt;br /&gt;
::* Wetterdaten werden über DWD Wetter-Stationen oder verschiedene API integriert. Es können verschiedene API zur Gewinnung von Solar- und Wetterdaten kombiniert werden. &lt;br /&gt;
&lt;br /&gt;
::* Sprachensupport EN / DE&lt;br /&gt;
&lt;br /&gt;
::* die Prognosedaten, Wetterdaten, Verbraucherplanungen und die aktuellen Energieflüsse werden in umfangreich anpassbaren integrierten Grafiken dargestellt&lt;br /&gt;
&lt;br /&gt;
::* es wird keine externe SQL-Datenbank benötigt, die Datenhaltung erfolgt in einer Memory basierten Cachedatenbank inkl. einer Filesystempersistenz zur Datensicherung und Wiederherstellung beim FHEM-Restart&lt;br /&gt;
&lt;br /&gt;
::* die Integration von Geräten wie Wechselrichter, Energy Meter, Batterien, Wetterstationen oder Verbrauchern ist offen und universell gestaltet und bietet dem Anwender maximale Freiheiten bei der Einrichtung seines individuellen Solardatensystems.&lt;br /&gt;
&lt;br /&gt;
::* eine Integration von bis zu 4 Inverter und Smartloader (DC-DC Batterieladegeräte) ist möglich&lt;br /&gt;
&lt;br /&gt;
::* eine Integration von bis zu 3 nicht PV-Energieerzeugern wie BHKW, Windrädern oder Notstromaggregaten ist möglich&lt;br /&gt;
&lt;br /&gt;
::* eine integrierte und umfangreich anpassbare Verbrauchersteuerung vereint die PV Prognose basierte Einplanung der Verbraucher mit der Möglichkeit die Verbraucher durch das Modul schalten zu lassen und dadurch auf PV Erzeugungsschwankungen automatisch dynamisch zu reagieren&lt;br /&gt;
&lt;br /&gt;
::* Einbindung von bis zu drei Batteriespeichersystemen&lt;br /&gt;
&lt;br /&gt;
::* Bereitstellung von Leitwerten zur optimalen Einstellung/Steuerung von Batteriespeichersystemen&lt;br /&gt;
&lt;br /&gt;
::* trotz der hohen Komplexität wird dem Anwender durch eine &amp;quot;Guided Procedure&amp;quot; bei der Gerätedefinition der Einstieg erleichtert und damit ein optimales Benutzererlebnis geboten&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Abgrenzung:&#039;&#039;&#039; Das Modul 76_SolarForecast ist nicht zu verwechseln mit der SQL-basierten (DbLog) Prognose-Lösung mit Kostal Plenticore Wechselrichtern, die auf der eigenen Seite &#039;&#039;&#039;[[Kostal_Plenticore_10_Plus]]&#039;&#039;&#039; behandelt wird. Diese Lösung beschreibt kein monolithisches Modul, sondern basiert auf einer orchestrierbaren Zusammenstellung individueller Perl Programmbausteine. &lt;br /&gt;
&lt;br /&gt;
Im vorliegenden Wiki-Beitrag wird ausschließlich das Modul 76_SolarForecast behandelt.&lt;br /&gt;
&lt;br /&gt;
== Rahmenbedingungen und Voraussetzungen ==&lt;br /&gt;
&lt;br /&gt;
Das Wiki gibt allgemeingültige und teilweise sehr spezifische bzw. tiefgehende Informationen und Handlungsempfehlungen zum SolarForecast Modul. Die dargestellten Informationen beziehen sich auf den zum Zeitpunkt der Wiki-Erstellung vorhandenen Entwicklungsstand des Moduls. Durch die stetige Weiterentwicklung können sich Abweichungen zum beschriebenen Inhalt ergeben oder zur Obsoleszenz dieser Inhalte führen. &lt;br /&gt;
&lt;br /&gt;
Vergleiche deshalb die Informationen immer mit der aktuellen Befehls-Referenz zum Modul!&lt;br /&gt;
&lt;br /&gt;
== Definition ==&lt;br /&gt;
Ein SolarForecast Device wird mit dem Befehl&lt;br /&gt;
&lt;br /&gt;
 define &amp;lt;Name&amp;gt; SolarForecast &lt;br /&gt;
&lt;br /&gt;
angelegt. Nach der Definition wird der User durch einen Dialog geführt, der einige unerlässliche Einstellungen abfragt und in den entsprechenden Attributen und Readings persistiert. Alle Attribute und Readings die eine Einstellung der Anlage bewirken, sind mit dem Präfix &amp;quot;setup&amp;quot; versehen. &lt;br /&gt;
&lt;br /&gt;
Die Verwendung von setup-Readings gestattet es die Anlagenparameter dynamisch im Betrieb zu verändern. So kann mit &lt;br /&gt;
&lt;br /&gt;
 set &amp;lt;Name&amp;gt; setupStringAzimuth   bzw.&lt;br /&gt;
 set &amp;lt;Name&amp;gt; setupStringDeclination &lt;br /&gt;
&lt;br /&gt;
die Ausrichtung und der Neigungswinkel der PV-Module ständig angepasst werden, was zum Beispiel bei einer dem Sonnenstand nachgeführten Anlage von Bedeutung ist.&lt;br /&gt;
&lt;br /&gt;
Die abgefragten Einstellungen während des Dialoges sind ausführlich in den entsprechenden Attributen bzw. Set-Kommandos erläutert. Die benötigten Informationen sind anhängig von der gewählten Prognose-API und können im Umfang variieren.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2024-09-21 213257.png|right|thumb|400px|Anlagenprüfung über Drucktaste in der Kopfgrafik]]&lt;br /&gt;
&lt;br /&gt;
Sobald alle verlangten Einstellungen vorgenommen sind, sollte eine Anlagenprüfung mit &lt;br /&gt;
&lt;br /&gt;
 set &amp;lt;Name&amp;gt; plantConfiguration check&lt;br /&gt;
&lt;br /&gt;
oder über die angebotene Drucktaste in der Grafik durchgeführt werden. Es wird eine Plausibilitätsprüfung vorgenommen und das Ergebnis sowie eventuelle Hinweise bzw. Fehler ausgegeben.&lt;br /&gt;
Die Prüfung kann beliebig wiederholt werden und sollte sich bei jeder Einstellungsänderung der Anlage durchgeführt werden. Dadurch wird die Wahrscheinlichkeit von fehlerhaften Einstellungen minimiert.&lt;br /&gt;
&lt;br /&gt;
Neben den grundlegenden Einstellungen können im Device weitere sekundäre Anlagenkomponenten wie Batteriesystem(e) (Attribut setupBatteryDev1 - setupBatteryDev3), weitere Wetter-Devices (Attribut setupWeatherDev1 - setupWeatherDev3) oder sonstige Erzeuger (z.B. BHKW, Winderzeugung, Notstromaggregat) integriert werden (Attribut setupOtherProducer01 - setupOtherProducer03).&lt;br /&gt;
&lt;br /&gt;
Die verlangten Parameter der Anlage werden in der Online-Hilfe umfassend beschrieben und deren Funktion erschließt sich zum größten Teil selbst. Dennoch soll nachfolgend auf einige Punkte im Zusammenhang der Einrichtung eingegangen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Hinweise zur Struktur der Attribute ===&lt;br /&gt;
&lt;br /&gt;
Durch die umfangreichen Funktionen des Moduls würde bei Nutzung der im FHEM herkömmlich vorhandenen Attributstruktur die Anzahl der benötigten Attribute extrem stark steigen und nicht sinnvoll genutzt werden können. Deshalb gibt es neben den &#039;&#039;gewöhnlichen&#039;&#039; Attributen mit einfachen Werteauswahlmöglichkeiten oder Eingaben (z.B. ctrlDebug, ctrlSpecialReadings) sogenannte &#039;&#039;zusammengesetzte&#039;&#039; Attribute, deren Inhalt durch &#039;&#039;&#039;Schlüssel=Wert Paare&#039;&#039;&#039; gebildet wird und dadurch deutlich mehr Funktionen und Informationen transportieren als ein gewöhnliches Attribut.&lt;br /&gt;
&lt;br /&gt;
Für zusammengesetzte Attribute besteht die zusätzliche Möglichkeit, gewünschte Änderungen der Schlüssel=Wert Paare selektiv vornehmen zu können, ohne das gesamte Attribut aufrufen und ändern zu müssen. Die Vorgang, der vor allem für programmtechnische Manipulationen gedacht ist, kann durch eine Set-Funktion ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
 set &amp;lt;Name&amp;gt; attrKeyVal &amp;lt;Attribut&amp;gt; [&amp;lt;Gerät&amp;gt;] &amp;lt;Schlüssel=Wert&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiele für zusammengesetzte Attribute sind:&lt;br /&gt;
&lt;br /&gt;
* aiControl&lt;br /&gt;
* plantControl&lt;br /&gt;
* setup* Attribute&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Funktionen der Attribute werden durch einen Präfix geclustert und so inhaltlich, aber vor allem auch in der Attribute Drop-Down Liste, strukturiert dargestellt.&lt;br /&gt;
&lt;br /&gt;
Die Bedeutung der &#039;&#039;&#039;Präfixe&#039;&#039;&#039; sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;ai&#039;&#039;&#039; - Steuerungsfunktionen für KI (z.B. aiControl)&lt;br /&gt;
* &#039;&#039;&#039;consumer&#039;&#039;&#039; - Registrierung und Einstellungen für Verbraucher (z.B. consumer02, consumerControl)&lt;br /&gt;
* &#039;&#039;&#039;ctrl&#039;&#039;&#039; - Steuerungsfunktion allgemeiner Art (z.B. ctrlDebug, ctrlSpecialReadings, ctrlUserExitFn)&lt;br /&gt;
* &#039;&#039;&#039;flowGraphic&#039;&#039;&#039; - Einstellungen für die Energieflußgrafik (z.B. flowGraphicControl)&lt;br /&gt;
* &#039;&#039;&#039;graphic&#039;&#039;&#039; - Einstellung für die Grafik allgemein (z.B. graphicSelect, graphicControl)&lt;br /&gt;
* &#039;&#039;&#039;graphicBeam&#039;&#039;&#039; -  Einstellungen bestimmte Balkeneigenschaften in der Balkengrafik (z.B. graphicBeamHeightLevel1)&lt;br /&gt;
* &#039;&#039;&#039;plant&#039;&#039;&#039; - übergreifende Einstellungen betreffend der gesamten PV-Anlage bzw. des SolarForecast-Devices (z.B. plantControl)&lt;br /&gt;
* &#039;&#039;&#039;setup&#039;&#039;&#039; - Registrierung von Geräten in der Gesamtanlage und Einstellung deren Eigenschaften (Inverter, Zähler, Batterien usw.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Einrichtung der Wechselrichter mit setupInverterDevXX ===&lt;br /&gt;
&lt;br /&gt;
Mit den Attributen setupInverterDev01 bis setupInverterDevXX werden die vorhandenen Wechselrichter der Anlage registriert.&lt;br /&gt;
Das Modul unterscheidet verschiedene Wechselrichter-Typen, die sich durch ihre Arbeitsweise innerhalb der Anlage unterscheiden. Die Schlüssel=Wert Paare in den Attributen können mehrzeilig organisiert werden. Das erleichtert die Übersicht wenn viele dieser Parameter eingegeben werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;PV-Wechselrichter: Es ist der Standard Wechselrichter in einer PV-Anlage. An ihn sind die Solarzellen bzw. Strings angeschlossen. Die zugeordneten Strings können mit dem Schlüssel &#039;&#039;&#039;strings&#039;&#039;&#039; zugeordnet werden. Ohne diese Angabe werden diesem Gerät alle definierten Strings (siehe Attribut &#039;&#039;setupInverterStrings&#039;&#039;) zugeordnet. Der Wechselrichter liefert seine Energie in das Hausnetz. Alternativ kann mit der Angabe &#039;&#039;&#039;feed=grid&#039;&#039;&#039; die Funktion des Wechselrichters geändert werden. Diese Angabe sagt aus, dass dieser Wechselrichter ausschließlich in das öffentliche Netz einspeist.&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel-Setup eines PV-Wechselrichters (SMA STP):&lt;br /&gt;
&lt;br /&gt;
 STP_5000 pvIn=string_1_pdc:kW pvOut=total_pac:kW etotal=etotal:kWh capacity=5000 strings=Süddach asynchron=1 limit=100&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Solar-Ladegeräte: Solar-Ladegeräte sind DC-DC Wandler und liefern die Energie der angeschlossenen Solarzellen nicht in das Wechselstromnetz, sondern laden direkt eine Batterie bzw. versorgen einen Batteriewechselrichter auf der Gleichstromseite. Die Funktion als Solar-Ladegerät wird mit &#039;&#039;&#039;feed=bat&#039;&#039;&#039; aktiviert, die relevanten Strings können ebenfalls mit dem Schlüssel &#039;&#039;&#039;strings&#039;&#039;&#039; zugeordnet werden. Ein Beispiel eines Solar-Ladegerätes ist ein Victron SmartSolar MPPT. &lt;br /&gt;
&lt;br /&gt;
Ein Beispiel für das Setup eines Solar-Ladegeräts (SmartSolar Charger MPPT):&lt;br /&gt;
&lt;br /&gt;
 MQTT2_cerboGX_c0619ab34e08_solarcharger_Common pv=Yield_Power_value:W etotal=Yield_System_value:kWh capacity=2080 strings=Schleppdach feed=bat synchron=0 limit=100&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Batterie-Wechselrichter: Dieses Gerät hat keine angeschlossenen Solarzellen und arbeitet als DC-AC bzw. AC-DC Wandler zwischen einer Batterie (=Gleichstromquelle) und dem Wechselstrom-Hausnetz. Die Funktion als Batterie-Wechselrichter wird mit der Angabe &#039;&#039;&#039;strings=none&#039;&#039;&#039; aktiviert. Die Schlüssel &#039;&#039;etotal&#039;&#039; und &#039;&#039;pv&#039;&#039; können bei einem Batterie-Wechselrichter ohne zugeordnete Solarzellen nicht gesetzt werden. Ein Beispiel eines Batterie-Wechselrichter ist der Victron MultiPlus II.&lt;br /&gt;
&lt;br /&gt;
Ein Setup-Beispiel für einen Batterie-Wechselrichter (Victron MultiPlus II):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dc2ac=DC_IN:W&lt;br /&gt;
ac2dc=DC_OUT:W&lt;br /&gt;
capacity=7200&lt;br /&gt;
strings=none&lt;br /&gt;
icon=inverter@darkorange:inverter@grey&lt;br /&gt;
asynchron=0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Eigenschaften des Wechselrichters werden durch eine Reihe von &amp;lt;Schlüssel=Wert&amp;gt; Paaren festgelegt. Auf einige dieser Paramter soll hier eingegangen werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
==== Ausgewählte Schlüssel=Wert Paare ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;pvIn=&amp;lt;Readingname&amp;gt;:&amp;lt;Einheit&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
	&lt;br /&gt;
Dem Schlüssel &#039;&#039;pvIn&#039;&#039; wird ein Reading und dessen Einheit (W,kW) des Wechselrichter-Device zugeordnet, welches die aktuell zugeführte PV-Leistung bereitstellt. Der Schlüssel ist optional und dient in erster Linie dazu den entsprechenden Input-Wert in der Flußgrafik anzuzeigen sofern man das Attribut flowGraphicControl-&amp;gt;showGenerators=1 setzt. Dadurch ist auch der Eigenverbrauch des Inverters bzw. dessen Wandlungsverlust visuell abschätzbar.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;pvOut=&amp;lt;Readingname&amp;gt;:&amp;lt;Einheit&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
	&lt;br /&gt;
Dem Schlüssel &#039;&#039;pvOut&#039;&#039; wird ein Reading und dessen Einheit (W,kW) des Wechselrichter-Device zugeordnet, welches die aktuell erzeugte Leistung als positiven Wert liefert. Für einen PV-Wechselrichter (DC-&amp;gt;AC) oder Solar-Ladegerät (DC-&amp;gt;DC) ist es die erzeugte PV-Leistung.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;ac2dc=&amp;lt;Readingname&amp;gt;:&amp;lt;Einheit&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Dieser Schlüssel ist nur für einen Batterie-Wechselrichter setzbar. Das Reading liefert die aktuelle Leistung, die der Wechselrichter vom Hausnetz in Richtung Batterie (AC-&amp;gt;DC) wandelt. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;dc2ac=&amp;lt;Readingname&amp;gt;:&amp;lt;Einheit&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Dieser Schlüssel ist nur für einen Batterie-Wechselrichter setzbar. Das Reading liefert die aktuelle Leistung, die der Wechselrichter aus einer Gleichstromquelle, d.h. einer Batterie oder einem Solar-Ladegerät, in Richtung des Hausnetzes (AC) wandelt . &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;capacity=&amp;lt;max. WR-Leistung&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In dem Schlüssel &#039;&#039;capacity&#039;&#039; wird die maximal mögliche Leistung in Watt (ohne Einheit) angegeben, die der Wechselrichter lt. seinem Datenblatt in der Lage ist zu leisten. &lt;br /&gt;
&lt;br /&gt;
Die Angabe &#039;&#039;capacity&#039;&#039; hat in erster Linie die Aufgabe, die max. mögliche PV-Leistung für die Prognose zu begrenzen. Oft sind die installierten Peak-Leistungen der PV-Module höher als die angeschlossene Wechselrichter-Leistung bzw. kommt es vor, dass die KI / (korrigierte) API-Prognose über der maximal möglichen Wechselrichter-Leistung liegt. In diesen Fällen wird die Prognose auf die angegebene &#039;&#039;capacity&#039;&#039; begrenzt.&lt;br /&gt;
&lt;br /&gt;
Der Wert in diesem Schlüssel kann, wie andere Schlüssel auch, dynamisch zur Laufzeit mit dem Set-Befehl &amp;quot;[[#Hinweise zur Struktur der Attribute|set &amp;lt;Name&amp;gt; attrKeyVal ...]]&amp;quot; geändert werden. Ein Beispiel zur Anwendung ergibt sich aus folgendem Szenario.&lt;br /&gt;
&lt;br /&gt;
Gegeben sei eine Anlage die aus Solarzellen (1300 W Peak) mit einem Solar-Ladegerät mit max. 1800 W Leistung, einer Batterie und einem Batterie-Wechselrichter besteht. Die Anlage versorgt nur das Hausnetz. Eine Netzeinspeisung ist nicht vorgesehen oder nicht erlaubt.  &lt;br /&gt;
&lt;br /&gt;
Nehmen wir an, es ist ein Tag voller Sonnenschein prognostiziert und die Batterie ist zunächst entladen. Der Haushalt verbraucht 300 W. Dann würden die PV-Module Module ihre volle Leistung von 1300 W Peak leisten können, die sich auch in der Balken-Vorhersage niederschlägt. Die Leistung wird nicht begrenzt, weil:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* sie niedriger ist als die nominale und angegebene Wechselrichter Kapazität von &#039;&#039;capacity=1800&#039;&#039;&lt;br /&gt;
* die erzeugte Energie anteilig für die Verorgung des Hauses und die Aufladung der Batterie verwendet wird&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Anders verhält es sich wenn nach einer gewissen Zeit die Batterie voll ist und weiterhin volle PV Leistung zur Verfügung steht. In diesem Fall würde der Haushalt seine benötigte Leistung von 300 W abnehmen, aber das Solar-Ladegerät kann keine weitere Leistung mehr abgeben, da die Batterie bereits voll geladen ist, d.h. die Anlagensteuerung würde die PV-Anlage abregeln. In diesem Fall, wenn es einen längeren Zeitraum betrifft, muß auch die Prognose PV-Energie auf 300 Wh (bzw. den durchschnittlichen Energieverbrauch des Hauses pro Stunde) begrenzt werden, damit die PV-Prognose nach unten angepasst wird und so eine realitätsnahere Prognose ermöglicht wird. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;icon=&amp;lt;aktiv&amp;gt;[@&amp;lt;Farbe&amp;gt;][:&amp;lt;inaktiv&amp;gt;[@&amp;lt;Farbe&amp;gt;]]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Jeder Wechselrichter wird im Standard durch ein Icon getrennt nach Tag und Nacht, d.h. je nach Aktiv-Status und Inaktiv-Status, symbolisiert.&lt;br /&gt;
&lt;br /&gt;
;Tag bzw. Aktivität: Für Wechselrichter mit angeschlossenen PV-Modulen wird ein entsprechend gefärbtes Sonnensymbol im Standard verwendet. Batterie-Wechselrichter verwenden ein gefärbtes Invertersymbol.&lt;br /&gt;
&lt;br /&gt;
;Nacht bzw. Inaktivität: Für Wechselrichter mit angeschlossenen PV-Modulen wird ein entsprechend gefärbtes Symbol der aktuellen Mondphasen im Standard verwendet. Batterie-Wechselrichter verwenden ein ausgegrautes Invertersymbol.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wie angegeben, kann man für Tag und Nacht jeweils ein Wunsch-Icon, wenn gewünscht mit einer alternativen Färbung, auswählen.&lt;br /&gt;
&lt;br /&gt;
 icon=inverter@darkorange:inverter@gray&lt;br /&gt;
&lt;br /&gt;
Soll nur das Icon für die Nacht bzw. Inaktivät geändert werden, kann dies in dieser Form angegeben werden:&lt;br /&gt;
&lt;br /&gt;
 icon=:inverter@gray  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
(beachte den führenden &#039;:&#039;)&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
==== Definition von Hybrid-Wechselrichtern  ====&lt;br /&gt;
&lt;br /&gt;
Zur Zeit der Erstellung dieses Abschnittes ist es in der SolarForecast Version 1.58.x nicht möglich, einen Hybridwechselrichter nativ zu definieren. Als Workaround wird eine passende Kombination aus PV-Wechselrichter und Batterie-Device oder einer Kombination aus PV-Wechselrichter + Batterie-Wechselrichter und Batterie-Device erstellt.&lt;br /&gt;
&lt;br /&gt;
Bei allen benutzten Methoden ist es sehr wichtig!, dass die geforderten Inhalte der jeweilige Attributschlüssel beachtet und eingehalten werden. So ist zum Beispiel die Angabe von:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;pvOut&#039;&#039;&#039; - Ein Reading, welches die aktuelle Leistung aus PV-Erzeugung, die an das Hausnetz oder öffentliche Netz geliefert wird, bereitstellt. Es wird ein positiver numerischer Wert erwartet.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
bei einem PV-Wechselrichter ein Reading welches ausschließlich die Leistung liefert, die von den Solarzellen-Generatoren erzeugt wird. Elemente von Batterieleistungen oder Gridbestandteile dürfen hier nicht enthalten sein. Sind in den Gerätemodulen diese Readings in dieser Form nicht enthalten, bietet es sich an, userReadings zur Erstellung von zusätzlichen Readings in den Quellendevices zu nutzen um den nötigen Input für SolarForecast bereitzustellen. &lt;br /&gt;
&lt;br /&gt;
So wird zum Beispiel bei einem 	Batterie-Wechselrichter gefordert, dass die Readings &#039;&#039;&#039;ac2dc&#039;&#039;&#039; (AC-&amp;gt;DC-Leistung Hausnetz zur Batterie) und &#039;&#039;&#039;dc2ac&#039;&#039;&#039; (DC-&amp;gt;AC-Leistung (Batterie zum Hausnetz) &#039;&#039;&#039;jeweils als als positiver Wert&#039;&#039;&#039; anzugeben ist. Manche Batterie-Devices stellen allerdings nur ein Reading zur Verfügung, welches vorzeichenbehaftet die Leistungen in die Batterie bzw. aus der Batterie heraus liefert. Mit einem userReadings Attribut kann aus diesem Reading zwei neue Readings erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;Name&amp;gt; userReadings  BatIn:DC_Power_value.* {&lt;br /&gt;
                          my $pwr = ReadingsVal ($name, &#039;DC_Power_value&#039;, 0);&lt;br /&gt;
                          $pwr    = $pwr &amp;gt; 0 ? $pwr : 0;&lt;br /&gt;
                          $pwr&lt;br /&gt;
                          },&lt;br /&gt;
                          BatOut:DC_Power_value.* {&lt;br /&gt;
                          my $pwr = ReadingsVal ($name, &#039;DC_Power_value&#039;, 0);&lt;br /&gt;
                          $pwr    = $pwr &amp;lt; 0 ? - $pwr : 0;&lt;br /&gt;
                          $pwr&lt;br /&gt;
                          },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist das Quellenreading &#039;&#039;DC_Power_value&#039;&#039; positiv, stellt der Wert AC-&amp;gt;DC-Leistung dar und wird als neues Reading BatIn bereitgestellt. Mit einem negativen Vorzeichen ist es eine DC-&amp;gt;AC-Leistung und wird in dem neuen Reading BatOut &#039;&#039;&#039;ebenfalls als positiver Wert&#039;&#039;&#039; bereitgestellt. Auch hier gilt der Grundsatz, dass zum Beispiel BatOut nur die von der Batterie gelieferte Energie und keinen Mix aus Batterie- und PV-Energie enthalten darf.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
===== Integration eines DEYE SUN-12K-SG04LP3-EU =====&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2025-09-23 203955.png|right|thumb|400px|DEYE SUN-12K-SG04LP3-EU Grundlegende Systemarchitektur]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der DEYE vereint wie wohl alle bzw. die meisten Hybrid-Wechselrichter die Anschlüsse:&lt;br /&gt;
&lt;br /&gt;
* Eingang Solar-Strings&lt;br /&gt;
* Anschluß Batteriespeicher&lt;br /&gt;
* AC-Anschluß öffnetliches Netz&lt;br /&gt;
* AC-Anschluß Ersatzlast (wird bei Netzausfall weiter versorgt)&lt;br /&gt;
* AC-Anschluß Notstromgenerator bzw. netzgekoppelter anderer Wechselrichter (hier ist ein Growatt Micro-Wechselrichter angeschlossen)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Für die Integration werden die setup-Attribute durch das FHEM Device &#039;&#039;Deye_Inverter&#039;&#039; besetzt:&lt;br /&gt;
&lt;br /&gt;
* setupInverterStrings zur Definition der vorhandenen Solarstrings&lt;br /&gt;
* setupInverterDev01 zur Definition der Inverteranschlüsse und Eigenschaften&lt;br /&gt;
* setupBatteryDev01 integriert die Batterie&lt;br /&gt;
* setupMeterDev liefert die Daten für Netzbezug und Netzeinspeisung&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das nachfolgend verwendete Device &#039;&#039;Deye_Inverter&#039;&#039; ist ein MQTT-Device. Es bekommt Daten vom Deye-Hybridwechselrichter per MQTT und sendet Einstellungen zum Deye, bspw. maximaler Lade/Entladestrom des Akkus, Laden des Akkus aus dem Netz, Ein- und Ausschalten des Micro-Inverter-Ports.&lt;br /&gt;
Das Reading &#039;&#039;Deye_Growatt_power&#039;&#039; im Schlüssel &#039;&#039;pvOut&#039;&#039; ist eine Addition aus den Daten des Deye WR und des angeschlossenen Growatt Micro-Wechselrichters.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;Name&amp;gt; setupInverterStrings Sueddach,Garagendach&lt;br /&gt;
attr &amp;lt;Name&amp;gt; setupInverterDev01 Deye_Inverter pvOut=Deye_Growatt_power:W capacity=15200 etotal=total_pv_production:kWh&lt;br /&gt;
attr &amp;lt;Name&amp;gt; setupBatteryDev01 Deye_Inverter pin=-pout pout=battery_output_power:W intotal=total_charge_of_the_battery:kWh outtotal=total_discharge_of_the_battery:kWh cap=15200 charge=SOC_jkbms&lt;br /&gt;
attr &amp;lt;Name&amp;gt; setupMeterDev Deye_Inverter gcon=total_grid_power:W contotal=total_energy_bought:kWh gfeedin=-gcon feedtotal=total_energy_sold:kWh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;setupInverterStrings&#039;&#039; benennt alle vorhandenen Solarstrings. Werden mehrere Inverter definiert, kann mit dem hier nicht verwendeten Schlüssel &#039;&#039;strings&#039;&#039; eine Zuordnung der Strings zum angeschlossenen Inverter vorgenommen werden.&lt;br /&gt;
&lt;br /&gt;
Im Attribut &#039;&#039;setupInverterDev01&#039;&#039; ist der allgemeine Schlüssel &#039;&#039;capacity&#039;&#039; zur Festlegung der Inverterleistung von 15200 Watt gesetzt. Die spezifischen Schlüssel &#039;&#039;pvOut&#039;&#039; und &#039;&#039;etotal&#039;&#039; teilen dem Modul die aktuell erzeugte PV-Leistung sowie die gesamte erzeugte PV-Energie mit. Letzteres dient unter anderem dazu die stündlich real erzeugte PV-Energie festzuhalten und mit der Prognose zu vergleichen. Die in den schlüsseln hinterlegten dürfen keine Battrie- oder Netzanteile enthalten.&lt;br /&gt;
&lt;br /&gt;
Das Attr &#039;&#039;setupBatteryDev01&#039;&#039; implementiert alle relevanten Batteriewerte. Das Reading &#039;&#039;battery_output_power&#039;&#039; im Schlüssel &#039;&#039;pout&#039;&#039; liefert den Batterie-Output als positiven Wert. Als Besonderheit kann der Wert &#039;&#039;pin&#039;&#039;, die Batterie-Ladeleistung, den Wert des Readings in &#039;&#039;pout&#039;&#039; übernehmen wenn dieser Wert ein negatives Vorzeichen hat. Die Schlüssel &#039;&#039;intotal&#039;&#039; und &#039;&#039;outtotal&#039;&#039; liefern die summierten Totalwerte für Batterie Ladung bzw. Entladung. Die Kapazität &#039;&#039;cap&#039;&#039; wird für verschiedene Aspekte der SoC- und Ladesteuerung verwendet. &lt;br /&gt;
&lt;br /&gt;
Abschließend liefern die angebenen Readings in den Schlüsseln des Attributes &#039;&#039;setupMeterDev&#039;&#039; alle notwendigen Werte des Netzbezugs und der Netzeinspeisung.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nachfolgende Links liefern &#039;&#039;&#039;weitere Infos&#039;&#039;&#039; wie man Daten aus dem Deye WR ausliest und Einstellungen des Deye WR verändert:&lt;br /&gt;
&lt;br /&gt;
* https://github.com/klatremis/esphome-for-deye&lt;br /&gt;
* https://github.com/philipphenkel/esphome-config&lt;br /&gt;
* https://github.com/bagges/deye-esp32-bridge&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Für eine weiterführende Automation soll der Akku netzdienlich in der Mittagszeit geladen werden. Dazu wurde durch den Anwender folgendes userReadings &#039;&#039;MaxBattCharge_Request&#039;&#039; im Device &#039;&#039;Deye_Inverter&#039;&#039; für den maximalen Ladestrom definiert (bei 90% SOC und bei 100% SOC):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MaxBattCharge_Request:SOC_jkbms.* {&lt;br /&gt;
                                    if ((ReadingsNum(&#039;mySolarForecast&#039;,&#039;RestOfDayPVforecast&#039;,&#039;&#039;)&lt;br /&gt;
                                         - ReadingsNum(&#039;mySolarForecast&#039;,&#039;special_todayConForecastTillSunset&#039;,&#039;&#039;)&lt;br /&gt;
                                         - (90-ReadingsNum(&#039;JK_BMS&#039;,&#039;capacity_remaining&#039;,&#039;&#039;))/100*310*51.2) &amp;lt; 400&lt;br /&gt;
                                         and ReadingsAge(&#039;JK_BMS&#039;,&#039;capacity_full_timestamp&#039;,&#039;&#039;) &amp;lt; 561600)&lt;br /&gt;
                                    {100}&lt;br /&gt;
                                    elsif ((ReadingsNum(&#039;mySolarForecast&#039;,&#039;RestOfDayPVforecast&#039;,&#039;&#039;)&lt;br /&gt;
                                            - ReadingsNum(&#039;mySolarForecast&#039;,&#039;special_todayConForecastTillSunset&#039;,&#039;&#039;)&lt;br /&gt;
                                            - (100-ReadingsNum(&#039;JK_BMS&#039;,&#039;capacity_remaining&#039;,&#039;&#039;))/100*310*51.2) &amp;lt; 400&lt;br /&gt;
                                            and ReadingsAge(&#039;JK_BMS&#039;,&#039;capacity_full_timestamp&#039;,&#039;&#039;) &amp;gt;= 561600)&lt;br /&gt;
                                    {100}&lt;br /&gt;
                                    elsif ((ReadingsNum(&#039;mySolarForecast&#039;,&#039;RestOfDayPVforecast&#039;,&#039;&#039;)&lt;br /&gt;
                                            - ReadingsNum(&#039;mySolarForecast&#039;,&#039;special_todayConForecastTillSunset&#039;,&#039;&#039;)&lt;br /&gt;
                                            - (90-ReadingsNum(&#039;JK_BMS&#039;,&#039;capacity_remaining&#039;,&#039;&#039;))/100*310*51.2) &amp;gt;= 400&lt;br /&gt;
                                            and ReadingsAge(&#039;JK_BMS&#039;,&#039;capacity_full_timestamp&#039;,&#039;&#039;) &amp;lt; 561600)&lt;br /&gt;
                                    {ceil ((90-ReadingsNum(&#039;JK_BMS&#039;,&#039;capacity_remaining&#039;,&#039;&#039;))/100*310/3.5)}&lt;br /&gt;
                                    elsif ((ReadingsNum(&#039;mySolarForecast&#039;,&#039;RestOfDayPVforecast&#039;,&#039;&#039;)&lt;br /&gt;
                                            - ReadingsNum(&#039;mySolarForecast&#039;,&#039;special_todayConForecastTillSunset&#039;,&#039;&#039;)&lt;br /&gt;
                                            - (100-ReadingsNum(&#039;JK_BMS&#039;,&#039;capacity_remaining&#039;,&#039;&#039;))/100*310*51.2) &amp;gt;= 400&lt;br /&gt;
                                            and ReadingsAge(&#039;JK_BMS&#039;,&#039;capacity_full_timestamp&#039;,&#039;&#039;) &amp;gt;= 561600)&lt;br /&gt;
                                    {ceil ((100-ReadingsNum(&#039;JK_BMS&#039;,&#039;capacity_remaining&#039;,&#039;&#039;))/100*310/3.0)}&lt;br /&gt;
                                  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das userReading wird in einer Automation benutzt, um den maximalen Ladestrom einzustellen; Ladebeginn bei ausreichend Sonne ist ab 11:30 eingestellt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
===== Integration eines Fronius Symo GEN24 10.0 Plus mit (virtuellen) Batterie-Wechselrichter =====&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2025-10-04 162422.png|right|thumb|400px|Flußgrafik mit eingebauten Hybrid-Wechselrichter]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Integration des Wechselrichters Fronius Symo GEN24 10.0 Plus benötigt zunächst eine Anbindung in FHEM, was mit dem Modul 98_fronius.pm vorgenommen wurde.&lt;br /&gt;
&lt;br /&gt;
Das Ziel der Kurzdokumentation ist neben der Darstellung eines Hybridwechselrichters aufzuzeigen, wie man den Wandlungsverlust eines PV-Wechselrichters (pvIn – pvOut) in Näherung visualisieren kann.  &lt;br /&gt;
&#039;&#039;&#039;pvIn&#039;&#039;&#039; ist dabei die Summe der Produkte aus den DC Strom- und Spannungswerten der einzelnen Strings (hier 2109 W), &#039;&#039;&#039;pvOut&#039;&#039;&#039; (hier 2065 W) ist die an den Inverterknoten (hier 1105 W) plus die an die Batterie abgegebene Leistung (hier 960 W). Bei dieser Implementierung wird mit einem zusätzlichen (virtuellen) Batterie-Wechselrichter gearbeitet. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In Vorbereitung werden die beiden anzugebenden Readings &#039;&#039;&amp;lt;pvIn&amp;gt;&#039;&#039; und &#039;&#039;&amp;lt;pvOut&amp;gt;&#039;&#039; zum Beispiel als userReadings erstellt, wobei &#039;&amp;lt;pvIn&amp;gt;&#039;&#039; und &#039;&#039;&amp;lt;pvOut&amp;gt;&#039;&#039; natürlich durch Readingnamen ersetzt werden müssen. Diese beiden Readings werden wie folgt berechnet / erstellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;pvOut&amp;gt; = Inverter_Common_PAC_Value - PowerFlow_Site_P_Akku&lt;br /&gt;
&amp;lt;pvIn&amp;gt; = Inverter_Common_IDC_Value * Inverter_Common_UDC_Value + Inverter_Common_IDC_2_Value * Inverter_Common_UDC_2_Value&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;PV-Wechselrichter&#039;&#039;&#039; wird mit dem Attribut setupInverterDev01 definiert, wobei die angelegten Readingnamen in den Schlüsseln &#039;&#039;pvIn&#039;&#039; bzw. &#039;&#039;pvOut&#039;&#039; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SymGen24 icon=inverter@#ff8c00:inverter@grey capacity=10000 strings=suedwest,nordost etotal=User_Produced_PV:kWh pvOut=&amp;lt;pvOut&amp;gt;:W pvIn=&amp;lt;pvIn&amp;gt;:W&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dem PV-Wechselrichter sind die Strings &#039;&#039;suedwest&#039;&#039; und &#039;&#039;nordost&#039;&#039; zugewiesen.&lt;br /&gt;
&lt;br /&gt;
Der zusätzliche &#039;&#039;&#039;Batterie-Wechselrichter&#039;&#039;&#039; wird mit dem Attribut setupInverterDev02 hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SymGen24 icon=inverter@#ff8c00:inverter@grey strings=none ac2dc=-PowerFlow_Site_P_Akku:W  dc2ac=PowerFlow_Site_P_Akku:W capacity=7680&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bemerkungen:&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
- strings=none sowie die Schlüssel ac2cd und dc2ac sind typisch für einen Batterie-WR bzw. kennzeichnen einen WR als Batterie-Wechselrichter &amp;lt;br&amp;gt;&lt;br /&gt;
- die an die Batterie abgegebene Leistung ist negativ&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
===== Einbindung eines Fronius Symo GEN24 10.0 Plus mit BYD Batterie ohne (virtuellen) Batterie-Wechselrichter =====&lt;br /&gt;
Diee Implementierung ist komplett in diesem verlinkten [https://wiki.fhem.de/wiki/Solaranlage_Komplettbeispiel_Fronius_BYD Wiki Beitrag] beschrieben.&lt;br /&gt;
&lt;br /&gt;
Zur Integration des &#039;&#039;Fronius Symo&#039;&#039; in FHEM wird das Modul &#039;&#039;fronius.pm&#039;&#039; verwendet und in dem definierten Device &#039;&#039;Fronius_Symo_Gen24&#039;&#039; diverse userReadings erzeugt. Diese Readings werden in den korrespondierenden Schlüsseln des Attributes &#039;&#039;setupInverterDev01&#039;&#039; angegeben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
setupInverterDev01 Fronius_Symo_Gen24 etotal=User_Produced_PV_Energie:kWh pvOut=PowerFlow_Site_P_PV:W capacity=6000 strings=Dach_Ost,Dach_West&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Am Wechselrichter Fronius Symo Gen24 ist eine BYD Batterie angeschlossen. Das Device &#039;&#039;[https://wiki.fhem.de/wiki/Solaranlage_Komplettbeispiel_Fronius_BYD#PV_Batterie PV-Batterie]&#039;&#039; wird per [https://wiki.fhem.de/wiki/ModbusAttr ModbusAttr] angelegt und gemanaged. Die entsprechenden Readings werden im Attribut &#039;&#039;SetupBatteryDev01&#039;&#039; hinterlegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PV_Batterie&lt;br /&gt;
cap=10240&lt;br /&gt;
charge=BatteryChargePercent&lt;br /&gt;
icon=@dyn:@dyn:@dyn:@dyn&lt;br /&gt;
intotal=Summe_Ladung:Wh&lt;br /&gt;
outtotal=Summe_Entladung:Wh&lt;br /&gt;
pin=BatteryChargeWatt:W&lt;br /&gt;
pout=BatteryDischargeWatt:W&lt;br /&gt;
pinmax=10000&lt;br /&gt;
show=2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Einbindung eines SMA - Hybrid Sunny Tripower Smart Energy  =====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2025-10-05 083530.png|right|thumb|400px|SMAInverter.pm]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der genannte Wechselrichter wird über das Modul SMAInverter.pm in FHEM integriert. Die nebenstehende Abbildung zeigt und benennt die einzelnen Readings des Moduls welche in den entsprechenden Schlüsseln der SolarForecast Attribute &#039;&#039;setupInverterDevXX&#039;&#039; und &#039;&#039;setupBatteryDevXX&#039;&#039; zugeordnet werden. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Readings des SMAInverter Moduls können vor der Verwendung über geeignete Methoden (userReadings) in Readings mit sprechenden Namen überführt werden um sie mit diesen Namen in die genannten Attributschlüssel einzufügen. In der vorliegenden Lösung befinden sich die  Anwendungen SolarForecast und und die Inverter-Steuerung jeweils auf unterschiedlicher Hardware. Als Verbindungslayer wird MQTT genutzt, wobei dadurch bereits die dargestellten Readingnamen entstehen. &lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Der integrierte SolarForecast Grafikbereich ==&lt;br /&gt;
Das SolarForecast Modul beinhaltet eine Grafikdarstellung die sich in mehrere Bereiche gliedert. Diese Bereiche können über Attribute ein- bzw. ausgeblendet sowie die Inhalte festgelegt werden.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2024-09-21 223545.png|right|thumb|400px|Bereiche der integrierten Grafik]]&lt;br /&gt;
&lt;br /&gt;
Der nebenstehende Screenshot gibt die Gliederung der Grafikbereiche wieder:&lt;br /&gt;
&lt;br /&gt;
 1 - der Kopfbereich (Graphic Header)&lt;br /&gt;
 2 - durch den Nutzer selbst definierbarer Inhalt (Graphic Header Own Specification) als Teil des Kopfbereiches&lt;br /&gt;
 3 - Bereich Verbraucherdarstellung (Consumer Legend)&lt;br /&gt;
 4 - Bereich Balkengrafik (Beam Graphic)&lt;br /&gt;
 5 - Energieflußgrafik (Flow Graphic)&lt;br /&gt;
&lt;br /&gt;
Jeder dieser Bereiche hat spezifische Steuerungsattribute zur Anpassung des Verhaltens und ggf. des Inhalts.&lt;br /&gt;
&lt;br /&gt;
=== 1 - Der Grafik Kopfbereich ===&lt;br /&gt;
Im Kopfbereich befinden sich fest verankerte Informationen zur verwendeten Vorhersage-API sowie deren Status, Informationen zum Sonnenaufgang und Sonnenuntergang, dem aktuellen Gesamtstatus des SolarForecast Devices, Abweichungskennzahlen der PV Vorhersage sowie weitere Kennzeichen zur Verfügung.&lt;br /&gt;
Über zwei Drucktasten kann eine Anlagenprüfung durchgeführt und in den Spportthread des Moduls im FHEM Forum abgesprungen werden.&lt;br /&gt;
&lt;br /&gt;
Mit dem Attribut &#039;&#039;&#039;graphicHeaderShow&#039;&#039;&#039; kann der gesamte Kopfbereich 1 und 2 (nutzereigener Teilbereich) ausgeblendet werden. Wird der Kopbereich ausgeblendet, ist es hilfreich mit dem Attribut &#039;&#039;&#039;plantControl-&amp;gt;showLink&#039;&#039;&#039; einen Link zur Detailgrafik einzufügen da der in der Kopfgrafik integrierte Link in diesem Fall nicht mehr zur Verfügung steht. &lt;br /&gt;
&lt;br /&gt;
Das Attribut &#039;&#039;&#039;graphicHeaderDetail&#039;&#039;&#039; definiert welche Bereiche des Grafikkopfes angezeigt werden sollen. Es kann die Anzeige des Energieverbrauches, der PV-Erzeugung und der vom Nutzer selbst definierte Inhalt miteinander frei kombiniert werden.&lt;br /&gt;
&lt;br /&gt;
=== 2 - Der Bereich mit selbst definierbarem Inhalt (Graphic Header Own Specification) ===&lt;br /&gt;
In diesem Bereich kann der User beliebige Readings, Set-Kommandos und Attribute des SolarForecast Devices oder anderer Devices, die sich im gleichen FHEM-System wie das SolarForecast Device befinden, anzeigen lassen.&lt;br /&gt;
&lt;br /&gt;
Dieser Bereich ist als Tabelle mit einer beliebigen Anzahl von Zeilen organisiert. Jede Zeile beinhaltet ein Kommentarfeld und vier Nutzfelder zur Darstellung ausgewähler Werte + Label. Der Bereich bzw. der Inhalt des Bereiches wird mit dem Attribut &#039;&#039;&#039;graphicHeaderOwnspec&#039;&#039;&#039; definiert.  &lt;br /&gt;
&lt;br /&gt;
Das nachfolgende Beispiel zeigt ein real gesetztes graphicHeaderOwnspec Attribut. Die Leerzeilen sind nicht notwendig, erhöhen aber die Lesbarkeit der Struktur.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#&lt;br /&gt;
Autarkierate:Current_AutarkyRate&lt;br /&gt;
PV&amp;amp;nbsp;übermorgen:special_dayAfterTomorrowPVforecast&lt;br /&gt;
Verbrauch&amp;amp;nbsp;bis&amp;lt;br&amp;gt;Sunset:special_todayConForecastTillSunset&lt;br /&gt;
Verbrauch&amp;amp;nbsp;bis&amp;lt;br&amp;gt;nächsten&amp;amp;nbsp;Sunrise:special_conForecastTillNextSunrise&lt;br /&gt;
&lt;br /&gt;
#Batterie&lt;br /&gt;
BatteryLive&amp;amp;nbsp;Status:userFn_BatLive_State&lt;br /&gt;
SoC&amp;amp;nbsp;Limit:Settings_CGwacs_BatteryLife_MinimumSocLimit_value@MQTT2_cerboGX_c0619ab34e08_settings&lt;br /&gt;
SoC&amp;amp;nbsp;Ist:SOC_value@MQTT2_cerboGX_c0619ab34e08_battery&lt;br /&gt;
SOC&amp;amp;nbsp;Optimum:Battery_OptimumTargetSoC&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
aktuelle&amp;amp;nbsp;Ladung:userFn_Bat_EnergyState&lt;br /&gt;
Verfügbar:userFn_BatLive_UsableEnergy&lt;br /&gt;
benötigte&amp;amp;nbsp;Ladeenergie:userFn_Bat_EnergyRemain&lt;br /&gt;
Ladefreigabe:Battery_ChargeRecommended&lt;br /&gt;
&lt;br /&gt;
#&lt;br /&gt;
akt.&amp;amp;nbsp;DC&amp;amp;nbsp;Strom:DC_Current_value@MQTT2_cerboGX_c0619ab34e08_battery&lt;br /&gt;
Restladezeit&amp;amp;nbsp;Ziel:userFn_Bat_HoursUntilChargeFinish&lt;br /&gt;
MPII&amp;amp;nbsp;Ladestrom&amp;lt;br&amp;gt;Limit&amp;amp;nbsp;Soll:userFn_Bat_MaxChargeCurrent_set&lt;br /&gt;
:&lt;br /&gt;
&lt;br /&gt;
#Settings&lt;br /&gt;
Batterie&amp;amp;nbsp;Lademanagement:userFn_BatteryChargeManagement&lt;br /&gt;
SoC&amp;amp;nbsp;Management:userFn_BatterySoCManagement&lt;br /&gt;
MPII&amp;amp;nbsp;Ladestrom&amp;lt;br&amp;gt;Limit&amp;amp;nbsp;Ist:MaxChargeCurrent@MQTT2_cerboGX_c0619ab34e08_vebus&lt;br /&gt;
SmartSolar&amp;lt;br&amp;gt;Einspeisung:OvervoltageFeedIn@MQTT2_cerboGX_c0619ab34e08_settings&lt;br /&gt;
&lt;br /&gt;
MPII&amp;amp;nbsp;Limit&amp;lt;br&amp;gt;Einspeisung&amp;amp;nbsp;(W):MaxFeedInPower@MQTT2_cerboGX_c0619ab34e08_settings&lt;br /&gt;
SOC&amp;amp;nbsp;Limit&amp;amp;nbsp;Soll:MinimumSocLimit@MQTT2_cerboGX_c0619ab34e08_settings&lt;br /&gt;
Verbr.&amp;amp;nbsp;Neuplanung:consumerNewPlanning&lt;br /&gt;
Wetteranzeige:graphicShowWeather &lt;br /&gt;
&lt;br /&gt;
Anzeige&amp;amp;nbsp;Nachtstunden:graphicShowNight&lt;br /&gt;
hist.&amp;amp;nbsp;Stunden:graphicHistoryHour&lt;br /&gt;
Autokorrektur:pvCorrectionFactor_Auto&lt;br /&gt;
Victron&amp;amp;nbsp;BatteryLife&amp;lt;br&amp;gt;(1=an,10=aus):BatteryLife@MQTT2_cerboGX_c0619ab34e08_settings&lt;br /&gt;
&lt;br /&gt;
Grid&amp;amp;nbsp;Setpoint:GridSetpoint@MQTT2_cerboGX_c0619ab34e08_settings&lt;br /&gt;
Debug:ctrlDebu&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Kommentarfeld am Beginn einer Zeile wird durch ein &#039;&#039;&#039;#&#039;&#039;&#039; gekennzeichnet. Folgt dem # ein String, wird dieser String als Kommentarangabe gewertet und in der Grafik ausgegeben. Ein leeres Kommentarfeld wird lediglich durch ein # gefolgt von einem Leerzeichen oder NL beschrieben:&lt;br /&gt;
&lt;br /&gt;
 #:&amp;lt;Text&amp;gt;  (bzw nur # für einen &#039;leeren&#039; Titel)&lt;br /&gt;
&lt;br /&gt;
Ein Netzfeld bzw. ein Datensatz wird durch die Angabe von einem Label und dem darzustellenden Wert getrennt durch &#039;:&#039; definiert:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;Label&amp;gt;:&amp;lt;Reading, Attribut oder Set-Kommando&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das System erkennt selbständig, ob es sich bei der Angabe um ein Reading, Attribut oder ein Set-Kommando handelt. Sollen Readings, Set-Kommandos und Attribute anderer Devices als dem SolarForecast-Device angezeigt werden, kann dies einfach durch Zusatz von&lt;br /&gt;
&lt;br /&gt;
 ....@&amp;lt;Device-Name&amp;gt;&lt;br /&gt;
&lt;br /&gt;
an den Parameter erreicht werden.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2024-12-05 203916.png|right|thumb|400px|Darstellung eigener Grafikbereich]]&lt;br /&gt;
Die Eingabe kann mehrzeilig erfolgen. Werte mit den Einheiten &amp;quot;Wh&amp;quot; bzw. &amp;quot;kWh&amp;quot; werden entsprechend der Einstellung des Attributs &#039;&#039;&#039;graphicEnergyUnit&#039;&#039;&#039; umgerechnet. Im Label sind Leerzeichen durch &amp;quot;&amp;amp;amp;nbsp;&amp;quot; einzufügen, ein Zeilenumbruch durch &amp;quot;&amp;amp;lt;br&amp;amp;gt;&amp;quot;. Ein leeres Feld in einer Zeile wird durch &amp;quot;:&amp;quot; ohne eine weitere Angabe erzeugt.&lt;br /&gt;
&lt;br /&gt;
Die nebenstehende Abbildung zeigt die reale Darstellung der oben angegebenen Struktur im Attribut graphicHeaderOwnspec.&lt;br /&gt;
&lt;br /&gt;
==== Formatierung der Inhalte im Bereich &amp;quot;Graphic Header Own Specification&amp;quot; ====&lt;br /&gt;
&lt;br /&gt;
Die angezeigten Inhalte, wie z.B. Readingwerte, des User eigenen Bereiches (Attribut graphicHeaderOwnspec) können durch Spezifikationen im Attribut &#039;&#039;&#039;graphicHeaderOwnspecValForm&#039;&#039;&#039; manipuliert werden.&lt;br /&gt;
&lt;br /&gt;
Die Online-Hilfe zum Attribut graphicHeaderOwnspecValForm erläutert die verfügbaren Möglichkeiten und Optionen.&lt;br /&gt;
Als Beispiel hier einige mögliche Notationen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &#039;userFn_Bat_HoursUntilChargeFinish&#039;                        =&amp;gt; &amp;quot;($VALUE).&#039; Stunden&#039;&amp;quot;,&lt;br /&gt;
  &#039;MQTT2_cerboGX_c0619ab34e08_battery.SOC_value&#039;             =&amp;gt; &amp;quot;%.1f %%&amp;quot;,&lt;br /&gt;
  &#039;Settings_CGwacs_BatteryLife_MinimumSocLimit_value&#039;        =&amp;gt; &amp;quot;%.1f %%&amp;quot;,&lt;br /&gt;
  &#039;Info_MaxChargeCurrent_value&#039;                              =&amp;gt; &amp;quot;($VALUE).&#039; A&#039;&amp;quot;,&lt;br /&gt;
  &#039;DC_Current_value&#039;                                         =&amp;gt; &amp;quot;%.0f A&amp;quot;,&lt;br /&gt;
  &#039;MQTT2_cerboGX_c0619ab34e08_solarcharger_Common.Regulated&#039; =&amp;gt; &amp;quot;($VALUE eq &#039;1&#039; ? &#039;Ja&#039; : &#039;Nein&#039;)&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Eine Besonderheit besteht bei darzustellenden Werten die die Einheiten &#039;Wh&#039; bzw. &#039;kWh&#039; besitzen. Diese Werte und Einheiten werden automatisch entsprechend des gesetzten Attributes graphicEnergyUnit umgerechnet und angezeigt.&lt;br /&gt;
&lt;br /&gt;
Soll in diesen Fällen eine automatische Formatierung umgangen werden, kann eine solche Notation verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &#039;Today_PVforecast&#039; =&amp;gt; &amp;quot;(sprintf &#039;%.1f &amp;amp;amp;nbsp;kWh&#039;, ($VALUE / 1000))&amp;quot;,&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
Die Angabe von &amp;amp;amp;nbsp; vor &#039;kWh&#039; verhindert die Erkennung der Einheit und so auch die automatische Umsetzung durch das Modul. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 3 - Bereich Verbraucherdarstellung (Consumer Legend) ===&lt;br /&gt;
Dieser Grafikbereich zeigt die im Modul registrierten Verbraucher sowie deren Status und weitere Informationen an. Sind die Voraussetzungen gegeben, d.h. entsprechende Schlüssel in der ConsumerXX-Attributen gesetzt, können die Verbraucher über das Paneel manuell geschaltet werden.&lt;br /&gt;
&lt;br /&gt;
Zur Anpassung der Darstellung sind verschiedene Attribute verfügbar:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;consumerControl-&amp;gt;showLegend: Definiert die Lage bzw. Darstellungsweise der Verbraucherlegende sofern Verbraucher im SolarForecast Device registriert sind.&lt;br /&gt;
;consumerControl-&amp;gt;adviceIcon: Definiert die Art der Information über die geplanten Schaltzeiten eines Verbrauchers in der Verbraucherlegende. &lt;br /&gt;
;consumerControl-&amp;gt;detailLink: Wenn gesetzt, ist der jeweilige Verbraucher klickbar um direkt zur Detailansicht des jeweiligen Geräts auf einer neuen Browserseite zu gelangen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Im Standard wird das Verbraucherpaneel zwischen dem Kopfbereich und der ersten Balkengrafik Ebene dargestellt. Mit dem Attribut &#039;&#039;&#039;consumerControl-&amp;gt;showLegend&#039;&#039;&#039; und Argument &#039;&#039;&#039;icon_bottom&#039;&#039;&#039; oder &#039;&#039;&#039;text_bottom&#039;&#039;&#039; kann das Verbraucherpaneel an das Ende des Grafikbereiches verschoben werden.&lt;br /&gt;
Die Verwendung von &#039;&#039;&#039;text_bottom&#039;&#039;&#039; oder &#039;&#039;&#039;text_top&#039;&#039;&#039; blendet die Icon-Spalte im Verbraucherpaneel aus.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Screenshot 2025-02-08 093239.png|Darstellung mit consumerControl showLegend=icon_top (default)&lt;br /&gt;
File:Screenshot 2025-02-08 093646.png|Darstellung mit consumerControl showLegend=icon_bottom&lt;br /&gt;
File:Screenshot 2025-02-08 152018.png|Darstellung mit consumerControl showLegend=text_top bzw. text_bottom&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2025-02-08 213634.png|right|thumb|400px|]]&lt;br /&gt;
Ein Mouse-Over über das Uhrensymbol zeigt nähere Informationen zum Status, den Planungsdaten, Modus und weitere Daten des Verbrauchers. Der Schalter &amp;quot;Aus/Ein&amp;quot; visualisiert den Schaltzustand des Verbrauchers und können auch zum manuellen Schalten des Verbrauchers dienen. Voraussetzung ist, dass im consumer-Attribut die Schlüssel &amp;quot;on&amp;quot; und &amp;quot;off&amp;quot; mit den Set-Kommandos zum Schalten des Verbrauchers defininiert sind.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2025-02-08 222210.png|right|thumb|300px|]]&lt;br /&gt;
Der Auto-Schieberegler zeigt einerseits den Freigabestatus zum Schalten des Verbrauchers durch das SolarForecast Modul und kann andererseits manuell betätigt werden. Der Status des Reglers korrespondiert mit dem im zugehörigen Consumer Attribut vorhandenen Schlüssel &#039;&#039;&#039;auto&#039;&#039;&#039; angegebenen Reading-Wert.&lt;br /&gt;
&lt;br /&gt;
Die Verbraucherbezeichnungen in dem Paneel können in der Standardkonfiguration angeklickt werden um direkt zur Detailansicht des Devices zu gelangen. Mit dem Attribut &#039;&#039;&#039;consumerControl-&amp;gt;detailLink&#039;&#039;&#039; kann der Link zur Detailansicht deaktiviert werden.&lt;br /&gt;
&lt;br /&gt;
Das Uhrensymbol kann mit dem Attribut &#039;&#039;&#039;consumerControl-&amp;gt;adviceIcon&#039;&#039;&#039; geändert und/oder dessen Farbe angepasst, sowie einen Informationstext statt eines Icons angezeigt werden. &lt;br /&gt;
&lt;br /&gt;
=== 4 - Bereich Balkengrafik (Beam Graphic) ===&lt;br /&gt;
&lt;br /&gt;
Die Balkengrafik des Moduls ist ein wesentliches Visualisierungshilfsmittel. Im Auslieferungszustand existiert eine Balkengrafikebene, die &#039;&#039;&#039;Ebene 1&#039;&#039;&#039;.&lt;br /&gt;
Jede Ebene kann zwei Balkendiagramme mit auswählbaren Inhalten, Farben und einstellbarem LayoutTyp darstellen. In Ebene 1 kann die Wettervorhersage eingebettet oder, bei Vorhandensein von Batteriegeräten, die zukünftigen Zeiten mit empfohlener Ladefreigabe und SoC-Prognose eingeblendet werden. &lt;br /&gt;
&lt;br /&gt;
Welche Daten in welchen Zeiträumen dargestellt werden sollen, die Bestimmung des Layouts, die Gestaltung der Balken und einiges mehr wird durch Attribute gesteuert. Ein Teil dieser Einstellungen wird über das zusammengesetzete Attribut &#039;&#039;&#039;graphicControl-&amp;gt;&#039;Schlüssel&#039;=&#039;Wert&#039;&#039;&#039;&#039; eingestellt:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
::*&#039;&#039;&#039;grundsätzliche Layout Definitionen&#039;&#039;&#039;&lt;br /&gt;
[[Datei:Screenshot 2025-01-12 204342.png|right|thumb|400px|Standardanzeige mit Wetter-Icons, gesetzen Attributen graphicBeam1Color=D4C177, graphicHistoryHour=11]]&lt;br /&gt;
&lt;br /&gt;
;graphicControl-&amp;gt;beamPaddingTop:Legt den Platz in px im Balkendiagramm fest, der zwischen dem oberen Rand der jeweiligen Balkengrafik Ebene und der ersten Text- oder Iconreihe dieser Ebene eingefügt wird.&lt;br /&gt;
;graphicControl-&amp;gt;beamPaddingBottom:Legt den Platz in px im Balkendiagramm fest, der zwischen der letzten Text- oder Iconreihe der jeweiligen Balkengrafik Ebene und dem unteren Rand dieser Ebene eingefügt wird. &lt;br /&gt;
;graphicControl-&amp;gt;layoutType:Layout-Optionen zur grundlegenden Gestaltung der Balkengrafik&lt;br /&gt;
;graphicControl-&amp;gt;hourStyle:Format der Zeitangabe in der Balkengrafik&lt;br /&gt;
;graphicControl-&amp;gt;hourCount:Anzahl der darzustellenden Balken/Stunden in der Balkengrafk&lt;br /&gt;
[[Datei:Screenshot Screenshot 2025-05-21 153613.png|right|thumb|400px|Erweiterung des oberen und unteren Abstandes mit den Attributen graphicControl-&amp;gt;beamPaddingTop und graphicControl-&amp;gt;beamPaddingBottom]]&lt;br /&gt;
;graphicControl-&amp;gt;energyUnit:definiert die Einheit zur Anzeige der elektrischen Leistung in der Grafik&lt;br /&gt;
;graphicHistoryHour:Anzahl der vorangegangenen Stunden die in der Balkengrafik dargestellt werden&lt;br /&gt;
;graphicControl-&amp;gt;beamWidth:Vorgabe Breite der Balken der Balkengrafik in px (sonst automatische Bestimmung) &lt;br /&gt;
;graphicBeamHeightLevel1:Multiplikator zur Festlegung der maximalen Balkenhöhe der jeweiligen Ebene (hier Ebene 1)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
::*&#039;&#039;&#039;Definition der Inhalte und Balkeneigenschaften&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2025-01-12 204756.png|right|thumb|400px|Wie oben aber mit gesetzem Attribut graphicShowNight=1]]&lt;br /&gt;
;graphicBeam1Color:Farbauswahl des primären Balkens (Ebene 1)&lt;br /&gt;
;graphicBeam2Color:Farbauswahl des sekundären Balkens (Ebene 1)&lt;br /&gt;
;graphicBeam1Content:Legt den darzustellenden Inhalt des primären Balken fest (Ebene 1)&lt;br /&gt;
;graphicBeam2Content:Legt den darzustellenden Inhalt des sekundären Balken fest (Ebene 1)&lt;br /&gt;
;graphicBeam1FontColor:Auswahl der Schriftfarbe des primären Balkens (Ebene 1)&lt;br /&gt;
;graphicBeam2FontColor:Auswahl der Schriftfarbe des sekundären Balkens (Ebene 1)&lt;br /&gt;
[[Datei:Screenshot 2025-01-12 205024.png|right|thumb|400px|Balkengrafik mit aktivierter Ebene 2, gesetzem Attribut graphicShowNight=0 (ohne Zeitsynchronisation zwischen Ebene 1 und Ebene 2)]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
::*&#039;&#039;&#039;zusätzliche Optionen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;graphicShowNight:Anzeigen oder Verbergen der Nachtstunden in der Balkengrafik&lt;br /&gt;
;graphicShowWeather:Wettericons in der Balkengrafik anzeigen/verbergen (nur Ebene 1)&lt;br /&gt;
;graphicWeatherColor:Farbe der Wetter-Icons für die Tagesstunden&lt;br /&gt;
;graphicWeatherColorNight:Farbe der Wetter-Icons für die Nachtstunden&lt;br /&gt;
;graphicShowDiff:Zusätzliche Anzeige der Differenz &amp;quot;&amp;lt;primärer Balkeninhalt&amp;gt; - &amp;lt;sekundärer Balkeninhalt&amp;gt;&amp;quot; im Kopf- oder Fußbereich der Balkengrafik&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die genaue Beschreibung und deren Optionen sind der aktuellen Online-Hilfe zu entnehmen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Für alle Ebenen gilt:&#039;&#039;&#039; Ein Attribut mit einer ungeraden Balkennummer (z.B. graphicBeam1Content) bezieht sich immer auf den primären Balken, ein Attribut mit einer geraden Balkennummer (z.B. graphicBeam2Content) bezieht sich immer auf den sekundären Balken.&lt;br /&gt;
&lt;br /&gt;
Die Balken haben, wie die meisten Elemente in der Balkengrafik, eine Mouse-Over Eigenschaft welche relevante Informationen zu den dargestellten Werten anzeigt. &lt;br /&gt;
&lt;br /&gt;
Sind die Nachtunden mit &#039;&#039;&#039;graphicShowNight&#039;&#039;&#039; ausgeblendet, werden keine Balken angezeigt sofern es keine Werte anzuzeigen gibt. Sollten dennoch Werte ungleich 0 anzuzeigen sein, werden diese Werte auch trotz ausgeblendeter Nachstunden angezeigt. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Aktivierung der Balkengrafik Ebene 2 ====&lt;br /&gt;
[[Datei:Screenshot 2025-01-12 211741.png|right|thumb|400px|Balkengrafik mit aktivierter Ebene 2, gesetzem Attribut graphicShowNight=01 (mit Zeitsynchronisation zwischen Ebene 1 und Ebene 2)]]&lt;br /&gt;
&lt;br /&gt;
Um die &#039;&#039;&#039;Ebene 2&#039;&#039;&#039; der Balkengrafik zu aktivieren, setzt man das Attribut &#039;&#039;&#039;graphicBeam3Content&#039;&#039;&#039; und/oder &#039;&#039;&#039;graphicBeam4Content&#039;&#039;&#039; mit den gewünschten darzustellenden Inhalten. Die weiteren Darstellungsoptionen, wie graphicBeamCColor, können ebenfalls für den jeweiligen Balken selektiv definiert werden wenn ein Attribut mit der entsprechenden Nummer (z.B. 4 für Balken 4) vorhanden ist. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; Die Wetter-Icons können nur in Ebene 1 integriert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Werden in der Balkengrafik die Nachtstunden ausgeblendet (&#039;&#039;&#039;graphicShowNight=0&#039;&#039;&#039; bzw. default), findet zwischen der Ebene 1 und der Ebene 2 keine Synchronisation der Zeitebene statt. Falls es einen inhaltlichen Zusammenhang zwischen den Werten der Ebene 1 und Ebene 2 gibt, eine Zeitsynchronisation zwischen den Ebenen wünschenswert und sinnvoll. Durch Setzen des Attrbut &#039;&#039;&#039;graphicShowNight=01&#039;&#039;&#039; kann diese Synchronisation eingeschaltet werden. &lt;br /&gt;
&lt;br /&gt;
Dabei ist die Ebene 1 die führende Ebene, d.h. diese Ebene vererbt die exkludierten bzw. eventuell inkludierten Nachtstunden (wenn Werte in den Balken der Ebene 1 angezeigt werden sollen) an die nachfolgende Ebene.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
==== Aktivierung der Balkengrafik Ebene 3 ====&lt;br /&gt;
&lt;br /&gt;
Um die &#039;&#039;&#039;Ebene 3&#039;&#039;&#039; der Balkengrafik zu aktivieren, setzt man das Attribut &#039;&#039;&#039;graphicBeam5Content&#039;&#039;&#039; und/oder &#039;&#039;&#039;graphicBeam6Content&#039;&#039;&#039; mit den gewünschten darzustellenden Inhalten. Die weiteren Darstellungsoptionen, wie graphicBeamCColor, können ebenfalls für den jeweiligen Balken selektiv definiert werden wenn ein Attribut mit der entsprechenden Nummer (z.B. 5 für Balken 5) vorhanden ist. &lt;br /&gt;
&lt;br /&gt;
Werden in der Balkengrafik die Nachtstunden ausgeblendet (&#039;&#039;&#039;graphicShowNight=0&#039;&#039;&#039; bzw. default), findet zwischen der Ebene 1 und der Ebene 3 keine Synchronisation der Zeitebene statt. Falls es einen inhaltlichen Zusammenhang zwischen den Werten der Ebene 1 und Ebene 3 gibt, ist eine Zeitsynchronisation zwischen den Ebenen wünschenswert und sinnvoll. Durch Setzen des Attrbut &#039;&#039;&#039;graphicShowNight=01&#039;&#039;&#039; kann diese Synchronisation eingeschaltet werden. &lt;br /&gt;
&lt;br /&gt;
Dabei ist die Ebene 1 die führende Ebene, d.h. diese Ebene vererbt die exkludierten bzw. eventuell inkludierten Nachtstunden (wenn Werte in den Balken der Ebene 1 angezeigt werden sollen) an die nachfolgenden Ebenen, d.h. auch auf die Ebene 3.&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 5 - Die Energieflußgrafik (Flow Graphic) ===&lt;br /&gt;
[[Datei:Screenshot 2025-02-13 213424.png|right|thumb|400px|Energieflußgrafik mit Verbrauchern in der Nachtdarstellung (Mondphasen) nach Sonnenuntergang]]&lt;br /&gt;
&lt;br /&gt;
Die Energieflußgrafik stellt die registrierten Erzeuger, Batterien, Netzzugänge und Verbraucher strukturiert in mehreren Ebenen dar:&lt;br /&gt;
&lt;br /&gt;
;Ebene 1: In der oberen Ebene befinden sich die PV-Generatoren, Smart-Loader sowie sonstigen Erzeuger &lt;br /&gt;
;Ebene 2: Die mittlere Ebene organisiert den Zugang zum öffentlichen Netz, dem Hausknoten, dem Batterieknoten sowie einem Dummy-Verbraucher (Lampensymbol) für den Energieverbrauch des Hauses der keinem der registrierten Verbraucher zugeordnet werden kann.&lt;br /&gt;
;Ebene 3: In der unteren Ebene werden die registrierten Verbraucher dargestellt. Unterhalb der Verbrauchersymbole werden der aktuelle Verbrauch (bzw. der On/Off-Zustand) sowie darunter die Restlaufzeit (Minuten) des Verbrauchers, sofern er durch das Modul geplant und gestartet wurde. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Position und Gestaltung der Grafikelemente ist umfangreich über das Attribut &#039;&#039;&#039;flowGraphicControl&#039;&#039;&#039; und den darin verfügbaren Schlüsseln einstellbar.&lt;br /&gt;
Die Bedeutung der Schlüssel ist in der Online-Hilfe zum Attribut mit einem Beispiel beschrieben. &lt;br /&gt;
&lt;br /&gt;
Darüber hinaus enthalten die Setup-Attribute &#039;&#039;&#039;setupInverterDevXX&#039;&#039;&#039;, &#039;&#039;&#039;setupOtherProducerXX&#039;&#039;&#039; und &#039;&#039;&#039;consumerXX&#039;&#039;&#039; weitere Einstellungsmöglichkeiten um deren Icons und Färbungen in der Flußgrafik individuell einstellen zu können. So sind zum Beispiel die Icons der PV-Inverter und deren Färbung mit dem Schlüssel &#039;&#039;&#039;icon&#039;&#039;&#039; der setupInverterDevXX-Attribute in Abhängigkeit von Tag und Nacht separat definierbar.&lt;br /&gt;
&lt;br /&gt;
In den Verbraucher-Attributen ist mit dem Schlüssel &#039;&#039;&#039;noshow&#039;&#039;&#039; die Sichtbarkeit in der Flußgrafik definierbar.&lt;br /&gt;
&lt;br /&gt;
Generell steuert das Attribut &#039;&#039;&#039;graphicSelect&#039;&#039;&#039; die Ein- bzw. Ausblendung der gesamten Flußgrafik.&lt;br /&gt;
&lt;br /&gt;
== Die Verbrauchsprognose ==&lt;br /&gt;
Im Gegensatz zu der PV Prognose gibt es keine API oder andere externen Datenquellen die für die Verbrauchsprognose herangezogen werden könnten.&lt;br /&gt;
&lt;br /&gt;
Zum Zweck der Verbrauchsermittlung verwendet das Modul die Meterdaten aus dem Attribut &#039;&#039;setupMeterDev&#039;&#039; sowie den weiteren setup-Attributen &#039;&#039;setupInverterDevXX&#039;&#039;. &lt;br /&gt;
Sind auch andere Erzeuger (BHKW, Windanlagen, etc.) bzw. eine Batterie vorhanden, sind ebenfalls die Attribute &#039;&#039;setupOtherProducerXX&#039;&#039; bzw. &#039;&#039;setupBatteryDev&#039;&#039; für die Berechnung des Hausverbrauchs relevant. In diesen Attributen gibt es die Schlüssel &#039;&#039;*total&#039;&#039; die teilweise optional sind. Für die Verbrauchsprognose bilden sie jedoch die Berechnungsgrundlage und sind deswegen für diese Funktionalität wichtig anzugeben.&lt;br /&gt;
&lt;br /&gt;
Die Verbrauchswerte werden stundengenau erfasst und im Datenspeicher &#039;&#039;pvHistory&#039;&#039; für 31 Tage, sowie im Datenspeicher &#039;&#039;pvCircular&#039;&#039; 210 Tage gespeichert. Die beiden Datenspeicher übernehmen unterschiedliche Aufgaben im Rahmen der Prognoseerstellung. Auf Grundlage der gespeicherten und somit historischen Verbrauchswerte erstellt das Modul eine Prognose für die kommende Zeit.&lt;br /&gt;
&lt;br /&gt;
Mit dem Kommando &lt;br /&gt;
&lt;br /&gt;
   &amp;quot;get ... pvHistory [Tag]&amp;quot; &lt;br /&gt;
&lt;br /&gt;
wird der Inhalt aufgelistet. Die Schlüssel &#039;&#039;confc&#039;&#039;, &#039;&#039;con&#039;&#039; und &#039;&#039;gcon&#039;&#039; zeigen die Verbrauchsprognose, den Verbrauch und den Netzbezug der jeweiligen Stunde des gewählten Tages.&lt;br /&gt;
Die Stunde &amp;quot;99&amp;quot; zeigt die Summen dieser Schlüsssel für den Tag:&lt;br /&gt;
&lt;br /&gt;
      99 =&amp;gt; etotal: , pvfc: 782, pvrl: 0, rad1h: -&lt;br /&gt;
            confc: 14540, con: 13873, gcon: 13873, gfeedin: 0&lt;br /&gt;
            batintotal: , batin: 0, batouttotal: , batout: 0&lt;br /&gt;
            batmaxsoc: -, batsetsoc: -&lt;br /&gt;
            wid: , wcc: , wrp: , pvcorrf: , dayname: So&lt;br /&gt;
            cyclescsm01: 0&lt;br /&gt;
            cyclescsm02: 0&lt;br /&gt;
            cyclescsm03: 0&lt;br /&gt;
            cyclescsm04: 1, hourscsme04: 0.00&lt;br /&gt;
            cyclescsm05: 0&lt;br /&gt;
            cyclescsm06: 0&lt;br /&gt;
            cyclescsm07: 1, hourscsme07: 0.00&lt;br /&gt;
            cyclescsm08: 1, hourscsme08: 0.00&lt;br /&gt;
            cyclescsm09: 1, hourscsme09: 0.03 &lt;br /&gt;
&lt;br /&gt;
In dem Beispiel wurden für den Tag 14540 Wh Verbrauch prognostiziert, 13873 Wh tatsächlich verbraucht und davon 13873 Wh aus dem Netz bezogen. Leider war an diesem Tag keinerlei PV Ertrag vorhanden, was man auch an dem Schlüssel &#039;&#039;pvrl&#039;&#039; erkennen kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Den Inhalt des pvCircular-Speichers kann mit dem Befehl  &lt;br /&gt;
&lt;br /&gt;
     &amp;quot;get ... pvCircular [Tag]&amp;quot;&lt;br /&gt;
&lt;br /&gt;
angezeigt werden. Das nachfolgende Beispiel zeigt die Ausgabe für die Tagesstunde 23:&lt;br /&gt;
&lt;br /&gt;
 23 =&amp;gt; pvapifc: 0, pvaifc: -, pvfc: 0, aihit: 0, pvrl: 0&lt;br /&gt;
       batin01: 0, batin02: -, batin03: -&lt;br /&gt;
       batout01: 619, batout02: -, batout03: -&lt;br /&gt;
       confc: 650, gcon: 23, gfeedin: 0, wcc: 40, rr1c: 0.00&lt;br /&gt;
       temp: -6.00, wid: 100, wtxt: Bewölkungsentwicklung nicht beobachtet&lt;br /&gt;
       pprl01: -, pprl02: -, pprl03: -&lt;br /&gt;
       ... &lt;br /&gt;
       con_all =&amp;gt; Sa  @ 617 762&lt;br /&gt;
                  Di  @ 693 645&lt;br /&gt;
                  Mo  @ 620 679&lt;br /&gt;
                  So  @ 669 666 636&lt;br /&gt;
                  Mi  @ 701 626&lt;br /&gt;
                  Do  @ 612 642&lt;br /&gt;
                  Fr  @ 615 606&lt;br /&gt;
&lt;br /&gt;
Der Schüssel &#039;&#039;con_all&#039;&#039; enthält einen Hash aus Arrays die für jeden Wochentag (Mo..So) bis zu 30 Verbrauchswerte (in Wh) für die angezeigte Stunde speichern. Wie hier erkennbar ist, wurden an den drei vergangenen Sonntagen jweils 669 Wh, 666 Wh bzw. 636 Wh in der Stunde 23 (22:00-22:59) verbraucht.&lt;br /&gt;
Der Median aus diesen Werten geht in die Verbrauchsprognose ein. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; Die Wochentagsnamen Mo..So sind von den installierten Locales auf Betriebsystemebene abhängig. Ist keine deutsche locale installiert, werden die englischen Wochentagsnamen Mon..Sun verwendet. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Wie wird der Verbrauch des Hauses ermittelt und gespeichert? ===&lt;br /&gt;
Die Verbrauchsdaten des Hauses werden auf Stundenbasis ermittelt und gespeichert. Das &amp;quot;Haus&amp;quot; steht als Synonym aller im Kontext des Solarforecast Devices zusammengeführten Inverter (setupInverterDevXX), sonstigen Erzeuger (setupOtherProducerXX), Netzanschlüsse (setupMeterDev), Verbraucher (consumerXX) und Batterien (setupBatteryDev).&lt;br /&gt;
&lt;br /&gt;
Alle diese Daten werden durch das Modul mit jedem ausgeführten Zyklus (Attribut plantControl-&amp;gt;cycleInterval) und, wenn ein Event eines als &#039;asynchron&#039; definierten Devices auftritt, gesammelt und daraus permanent ein aktualisierter Verbrauch berechnet. Die Berechnung beschreibt folgende Formel:&lt;br /&gt;
&lt;br /&gt;
 PV-Erzeugung + sonstige Erzeugung - Netzeinspeisung + Netzbezug - Batterieladung + Batterieentladung - Verbrauch = 0&lt;br /&gt;
&lt;br /&gt;
bzw.&lt;br /&gt;
 &lt;br /&gt;
 Verbrauch (Wh) = PV-Erzeugung + sonstige Erzeugung - Netzeinspeisung + Netzbezug - Batterieladung + Batterieentladung&lt;br /&gt;
            con = PV           + PP                 - GridIn          + GridCon   - BatIn          + BatOut&lt;br /&gt;
&lt;br /&gt;
Die PV-Erzeugung beinhaltet die Summe aller durch die angegebnen Inverter (setupInverterDev01 .. setupInverterDevXX) erzeugten Energien. Die &amp;quot;sonstige Erzeugung&amp;quot; summiert sich aus den Werten aller angegebenen Producer (setupOtherProducer01 .. setupOtherProducerXX). Der Verbrauch wird durch die registrierten Geräte consumerXX teilweise direkt erfasst, ist aber grundsätzlich unvollständig da nicht alle Verbrauchswerte im &amp;quot;Haus&amp;quot; gemessen und durch das Modul erfasst werden können.&lt;br /&gt;
&lt;br /&gt;
Das folgende Beispiel zeigt das Ergebnis bei 300 Wh PV-Erzeugung und gleichzeitige 500 Wh Netzbezug:&lt;br /&gt;
&lt;br /&gt;
 Verbrauch = 300 + 0 - 0 + 500 - 0 + 0 = 800 Wh&lt;br /&gt;
&lt;br /&gt;
Bei 1300Wh PV-Erzeugung, 100 Wh Netzeinspeisung sowie 200 Wh Batterieladung ergibt sich der Verbrauch zu:&lt;br /&gt;
&lt;br /&gt;
 Verbrauch = 1300 + 0 - 100 + 0 - 200 + 0 = 1000 Wh&lt;br /&gt;
&lt;br /&gt;
Ohne Erzeugung, aber mit der Energielieferung von 500 Wh aus der Batterie, 800 Wh Netzbezug (mit einem Anteil Batterienachladung aus dem Netz von 200 Wh) sieht die Verbrauchsrechnung wie folgt aus: &lt;br /&gt;
&lt;br /&gt;
 Verbrauch = 0 + 0 - 0 + 800 - 200 + 500 = 1100 Wh&lt;br /&gt;
&lt;br /&gt;
Der so ermittelte Verbrauchsert wird mit jedem Zyklus in der pvHistory im Schlüssel &amp;quot;con&amp;quot; gespeichert. Man kann sich die gespeicherten Werte mit dem Befehl&lt;br /&gt;
&lt;br /&gt;
 get &amp;lt;name&amp;gt; pvHistory [&amp;lt;Tag&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
angezeigt werden. Nachfolgendes Beispiel zeigt einen Datensatz für die Stunde 8 eines Tages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      08 =&amp;gt; pvfc: 0, pvrl: 0, pvrlvd: 1, rad1h: -&lt;br /&gt;
            etotali01: 62940734, etotali02: 2928860, etotali03: -&lt;br /&gt;
            pvrl01: 0, pvrl02: 0, pvrl03: -&lt;br /&gt;
            etotalp01: -, etotalp02: -, etotalp03: -&lt;br /&gt;
            pprl01: -, pprl02: -, pprl03: -&lt;br /&gt;
            confc: 630, con: 962, gcons: 962, conprice: 0.2958&lt;br /&gt;
            gfeedin: 0, feedprice: 0.1269&lt;br /&gt;
            DoN: 0, sunaz: 120, sunalt: -3&lt;br /&gt;
            batintotal: 3713815.07742543, batouttotal: 3604468.26993988, batin: 0, batout: 0&lt;br /&gt;
            wid: 161, wcc: 89, rr1c: 0.00, pvcorrf: 0.27/0.00 temp: 7.40, &lt;br /&gt;
            csmt01: 110996.12, csme01: 32.1399999999994, minutescsm01: 30&lt;br /&gt;
            minutescsm02: 0&lt;br /&gt;
            csmt03: 2513.67, csme03: 0, minutescsm03: 0&lt;br /&gt;
            csmt04: 1581293.6, csme04: 136.5, minutescsm04: 60&lt;br /&gt;
            csmt05: 1662.17, csme05: 0, minutescsm05: 0&lt;br /&gt;
            csmt06: 90.53, csme06: 0, minutescsm06: 0&lt;br /&gt;
            csmt07: 39.75, csme07: 0, minutescsm07: 0&lt;br /&gt;
            csmt08: 38050, csme08: 0, minutescsm08: 0&lt;br /&gt;
            csmt09: 131627, csme09: 33.2000000000116, minutescsm09: 39&lt;br /&gt;
            minutescsm10: 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Schlüssel &amp;quot;con&amp;quot; ist der gespeicherte Verbrauch von 962 Wh ersichtlich.&lt;br /&gt;
&lt;br /&gt;
Zur Laufzeit bekommt man mit dem Attribut &#039;&#039;ctrlDebug=collectData&#039;&#039; einen Einblick in die Verbrauchsermittlung bei jedem Zyklus und kann so die Eingangswerte und das daraus resultierende Ergebnis im FHEM Log nachverfolgen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
2024.11.28 11:38:20.080 1: SolCast DEBUG&amp;gt; EnergyConsumption input -&amp;gt; PV: 96, PP: 0, GridIn: 0, GridCon: 582, BatIn: 5, BatOut: 0&lt;br /&gt;
2024.11.28 11:38:20.081 1: SolCast DEBUG&amp;gt; EnergyConsumption result -&amp;gt; 673 Wh&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wie wird die Verbrauchsprognose erstellt? ===&lt;br /&gt;
&lt;br /&gt;
Aus den gespeicherten con-Werten (Verbrauchsdaten) der pvCircular (Schlüssel con_all) sowie Einträgen der pvHistory wird der Median der historischen Tage ermittelt und für den kommenden Tag bzw. die kommen Stunden zur Prognose angewendet. Dabei geht per default jeder verfügbare historische Tag gleichberechtigt in die Prognose ein. Mit dem Attribut &#039;&#039;&#039;plantControl-&amp;gt;consForecastIdentWeekdays=1&#039;&#039;&#039; kann festgelegt werden, dass nur gleiche Wochentage (Mo..So) für die Prognose verwendet werden. Hierbei spielt die Vermutung, dass das Verbrauchsverhalten eine gewisse Korrelation zum Wochentag hat, eine Rolle. &lt;br /&gt;
&lt;br /&gt;
Verwendet man die Einstellung consForecastIdentWeekdays=1, hat es sich als vorteilhaft erwiesen, das Attribut &#039;&#039;&#039;plantControl-&amp;gt;consForecastLastDays&#039;&#039;&#039; auf einen niedrigeren Wert als den Default zu setzen. Eine Einstellung auf &#039;8&#039; zum Beispiel verwendet den Median der gleichen Wochentage der vergangenenen 8 Wochen. Dadurch wird nicht zu weit in die Vergangenheit verzweigt und so eine langsame Veränderung des Verbrauchsmedian, zum Beispiel hervorgerufen durch den Übergang vom Winter in das Frühjahr hinein, abgebildet.&lt;br /&gt;
&lt;br /&gt;
Weiteren Einfluß auf die Verbrauchsprognose kann mit dem Schlüssel &#039;&#039;&#039;exconfc&#039;&#039;&#039; im Consumerattribut genommen werden. Insbesondere bei der unregelmäßigen Nutzung von Verbrauchern mit hoher Leistung kann die Projektion der aufgezeichneten historischen Werte in die Prognose zu unverhältnismäßig hohen Verbrauchswerwartunden führen. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Möglicherweise führt in Fällen die Verwendung eine der nachfolgenden Optionen zu einem besseren Ergebnis:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;1 -&#039;&#039;&#039; die allgemeine Verbrauchsprognose wird um die gespeicherten Energieverbrauchsanteile reduziert.&lt;br /&gt;
* &#039;&#039;&#039;2 -&#039;&#039;&#039; wie bei &#039;1&#039;, jedoch gehen die Planungsdaten des Verbrauchers bei der Prognose der kommenden Stunden wieder mit ein.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wird &#039;&#039;&#039;exconfc&#039;&#039;&#039; in einem der Consumer verwendet, sollte&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;plantControl-&amp;gt;consForecastIdentWeekdays=1&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;plantControl-&amp;gt;consForecastLastDays=4&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
eingestellt werden. Hintergrund ist, dass die Verbrauchsanteile der jeweiligen Verbraucher nur maximal 31 Tage in der pvHistory verfügbar sind. Für längere Zeiträume stehen nur Gesamtsummen des Verbrauchs in der pvCircular zu Verfügung.  &lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;exconfc=1&#039;&#039;&#039; werden die in der Historie aufgezeichneten Verbrauchsbestandteile aus einer Prognose herausgerechnet. Konkret werden die in der pvHistory aufgezeichneten Verbrauchsbestandteile für die relevante Stunde ermittelt und daraus ein Durchschnitt gebildet der vom Prognosewert subtrahiert wird. Mit dem Attributwert plantControl-&amp;gt;consForecastIdentWeekdays=1 werden wiederum nur identische Wochentage (Mo .. So) zusammengefasst.&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;exconfc=2&#039;&#039;&#039; erfolgt ebenso die Eliminierung des Verbrauchsanteils, es wird jedoch auf die Planungsdaten für diesen Verbraucher zugegriffen und die geplanten Energieanteile wiederum der Verbrauchsprognose für die kommenden Stunden hinzugerechnet.&lt;br /&gt;
&lt;br /&gt;
In beiden Anwendungsfällen ist es notwendig, dass dem Modul durch die Angabe entsprechender Readings der Energieverbrauch im Setup des Consumers bekannt gemacht wird damit diese Energieanteile registriert werden können. Allgemein funktioniert die Verbrauchsprognose gut wenn Verbrauchsprozesse regelmäßig in einem wiederkehrenden Zeitraster ablaufen, der Verbrauch durch persönliche Rituale bestimmt ist. So zum Beispiel könnte am Dienstag und Samstag gewöhnlich die Waschmaschine laufen, das E-Auto täglich aufgeladen werden und gebügelt wird oft am Sonntag Nachmittag.&lt;br /&gt;
&lt;br /&gt;
Unregelmäßige Nutzung, vor allem vom Großverbrauchern, lässt sich nur sehr schwer und ungenügend auf zukünftige Verbrauchsprognosen projizieren.&lt;br /&gt;
&lt;br /&gt;
Das Ergebnis der Prognose wird im Kopf der Grafik in der Zeile &#039;&#039;Verbrauch&#039;&#039; sowie in diversen Readings wie &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;RestOfDayConsumptionForecast&#039;&#039;&#039; (Verbrauchsprognose für den Rest des Tages)&lt;br /&gt;
* &#039;&#039;&#039;NextHours_Sum04_ConsumptionForecast&#039;&#039;&#039; (Verbrauchsprognose für die nächsten 4 Stunden)&lt;br /&gt;
* &#039;&#039;&#039;Tomorrow_ConsumptionForecast&#039;&#039;&#039; (Verbrauchsprognose für den kommenden Tag)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
angezeigt.&lt;br /&gt;
&lt;br /&gt;
Weitere Aggregationen kann man sich über das Attribut &#039;&#039;&#039;ctrlStatisticReadings&#039;&#039;&#039; bei Bedarf zuschalten:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;conForecastTillNextSunrise&#039;&#039;&#039; - Verbrauchsprognose von aktueller Stunde bis zum kommenden Sonnenaufgang&lt;br /&gt;
* &#039;&#039;&#039;todayConForecastTillSunset&#039;&#039;&#039; - Verbrauchsprognose von aktueller Stunde bis Stunde vor Sonnenuntergang&lt;br /&gt;
* &#039;&#039;&#039;todayConsumptionForecast&#039;&#039;&#039; - Verbrauchsprognose aufgesplittet pro Stunde des aktuellen Tages (01-24) &lt;br /&gt;
 &lt;br /&gt;
==== Debugging der Verbrauchsprognose ====&lt;br /&gt;
Um sich einen Einblick über die Erstellung der Verbrauchsprognose zu verschaffen, bietet sich die Einschaltung von:&lt;br /&gt;
&lt;br /&gt;
 attr &amp;lt;Name&amp;gt; ctrlDebug consumption_long&lt;br /&gt;
&lt;br /&gt;
an. Mit diesem gesetzten Attribut wird die Erstellung der Prognose im Log protokolliert:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2025.02.27 19:52:00.063 1: SolCast DEBUG&amp;gt; ################### Start Consumption forecast ###################&lt;br /&gt;
2025.02.27 19:52:00.063 1: SolCast DEBUG&amp;gt; Basics - installed locale: de_DE.UTF-8, used scheme: DE&lt;br /&gt;
2025.02.27 19:52:00.064 1: SolCast DEBUG&amp;gt; process Today dayname: Do, Tomorrow dayname: Fr&lt;br /&gt;
2025.02.27 19:52:00.065 1: SolCast DEBUG&amp;gt; consumer &#039;03&#039; register for exclude day 13, hod: 01 - 74.43 Wh&lt;br /&gt;
2025.02.27 19:52:00.065 1: SolCast DEBUG&amp;gt; consumer &#039;03&#039; register for exclude day 13, hod: 10 - 290.77 Wh&lt;br /&gt;
2025.02.27 19:52:00.065 1: SolCast DEBUG&amp;gt; consumer &#039;03&#039; register for exclude day 13, hod: 24 - 660.70 Wh&lt;br /&gt;
2025.02.27 19:52:00.066 1: SolCast DEBUG&amp;gt; consumer &#039;03&#039; register for exclude day 20, hod: 07 - 459.93 Wh&lt;br /&gt;
2025.02.27 19:52:00.066 1: SolCast DEBUG&amp;gt; consumer &#039;07&#039; register for exclude day 20, hod: 02 - 0.33 Wh&lt;br /&gt;
2025.02.27 19:52:00.067 1: SolCast DEBUG&amp;gt; consumer &#039;03&#039; register for exclude day 30, hod: 08 - 264.78 Wh&lt;br /&gt;
2025.02.27 19:52:00.067 1: SolCast DEBUG&amp;gt; ################### Consumption forecast for the next Hours (new median) ###################&lt;br /&gt;
2025.02.27 19:52:00.067 1: SolCast DEBUG&amp;gt; excl. hist 74 Wh for Hour 01, Considered value numbers: 1&lt;br /&gt;
2025.02.27 19:52:00.067 1: SolCast DEBUG&amp;gt; estimated cons of Hour 01: 436 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.068 1: SolCast DEBUG&amp;gt; excl. hist 0 Wh for Hour 02, Considered value numbers: 1&lt;br /&gt;
2025.02.27 19:52:00.068 1: SolCast DEBUG&amp;gt; estimated cons of Hour 02: 428 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.068 1: SolCast DEBUG&amp;gt; estimated cons of Hour 03: 458 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.068 1: SolCast DEBUG&amp;gt; estimated cons of Hour 04: 503 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.069 1: SolCast DEBUG&amp;gt; estimated cons of Hour 05: 489 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.069 1: SolCast DEBUG&amp;gt; estimated cons of Hour 06: 482 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.069 1: SolCast DEBUG&amp;gt; excl. hist 460 Wh for Hour 07, Considered value numbers: 1&lt;br /&gt;
2025.02.27 19:52:00.069 1: SolCast DEBUG&amp;gt; estimated cons of Hour 07: 274 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.070 1: SolCast DEBUG&amp;gt; excl. hist 265 Wh for Hour 08, Considered value numbers: 1&lt;br /&gt;
2025.02.27 19:52:00.070 1: SolCast DEBUG&amp;gt; estimated cons of Hour 08: 441 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.070 1: SolCast DEBUG&amp;gt; estimated cons of Hour 09: 558 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.070 1: SolCast DEBUG&amp;gt; excl. hist 291 Wh for Hour 10, Considered value numbers: 1&lt;br /&gt;
2025.02.27 19:52:00.071 1: SolCast DEBUG&amp;gt; estimated cons of Hour 10: 443 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.071 1: SolCast DEBUG&amp;gt; estimated cons of Hour 11: 881 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.071 1: SolCast DEBUG&amp;gt; estimated cons of Hour 12: 794 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.072 1: SolCast DEBUG&amp;gt; estimated cons of Hour 13: 840 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.072 1: SolCast DEBUG&amp;gt; estimated cons of Hour 14: 660 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.072 1: SolCast DEBUG&amp;gt; estimated cons of Hour 15: 812 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.072 1: SolCast DEBUG&amp;gt; estimated cons of Hour 16: 752 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.073 1: SolCast DEBUG&amp;gt; estimated cons of Hour 17: 597 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.073 1: SolCast DEBUG&amp;gt; estimated cons of Hour 18: 652 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.073 1: SolCast DEBUG&amp;gt; estimated cons of Hour 19: 634 Wh, Considered value numbers: 4&lt;br /&gt;
2025.02.27 19:52:00.073 1: SolCast DEBUG&amp;gt; estimated cons of Hour 20: 628 Wh, Considered value numbers: 3&lt;br /&gt;
2025.02.27 19:52:00.074 1: SolCast DEBUG&amp;gt; estimated cons of Hour 21: 713 Wh, Considered value numbers: 3&lt;br /&gt;
2025.02.27 19:52:00.074 1: SolCast DEBUG&amp;gt; estimated cons of Hour 22: 596 Wh, Considered value numbers: 3&lt;br /&gt;
2025.02.27 19:52:00.074 1: SolCast DEBUG&amp;gt; estimated cons of Hour 23: 629 Wh, Considered value numbers: 3&lt;br /&gt;
2025.02.27 19:52:00.074 1: SolCast DEBUG&amp;gt; excl. hist 661 Wh for Hour 24, Considered value numbers: 1&lt;br /&gt;
2025.02.27 19:52:00.075 1: SolCast DEBUG&amp;gt; estimated cons of Hour 24: 660 Wh, Considered value numbers: 3&lt;br /&gt;
2025.02.27 19:52:00.075 1: SolCast DEBUG&amp;gt; ################### Consumption forecast for the next day (new median) ###################&lt;br /&gt;
2025.02.27 19:52:00.075 1: SolCast DEBUG&amp;gt; estimated cons Tomorrow: 16848 Wh, Individual hourly values considered: 72, exclude: 0 Wh (avg of 0 entities)&lt;br /&gt;
2025.02.27 19:52:00.075 1: SolCast DEBUG&amp;gt; ################### Store Consumption forecast values (new median) ###################&lt;br /&gt;
2025.02.27 19:52:00.076 1: SolCast DEBUG&amp;gt; store &#039;NextHour00&#039; hod &#039;20&#039; confc: 628, confcEx: 628&lt;br /&gt;
2025.02.27 19:52:00.076 1: SolCast DEBUG&amp;gt; store circular/history hod &#039;20&#039; confc: 628&lt;br /&gt;
2025.02.27 19:52:00.076 1: SolCast DEBUG&amp;gt; store &#039;NextHour01&#039; hod &#039;21&#039; confc: 713, confcEx: 713&lt;br /&gt;
2025.02.27 19:52:00.077 1: SolCast DEBUG&amp;gt; store circular/history hod &#039;21&#039; confc: 713&lt;br /&gt;
2025.02.27 19:52:00.077 1: SolCast DEBUG&amp;gt; store &#039;NextHour02&#039; hod &#039;22&#039; confc: 596, confcEx: 596&lt;br /&gt;
2025.02.27 19:52:00.077 1: SolCast DEBUG&amp;gt; store circular/history hod &#039;22&#039; confc: 596&lt;br /&gt;
2025.02.27 19:52:00.077 1: SolCast DEBUG&amp;gt; store &#039;NextHour03&#039; hod &#039;23&#039; confc: 629, confcEx: 629&lt;br /&gt;
2025.02.27 19:52:00.078 1: SolCast DEBUG&amp;gt; store circular/history hod &#039;23&#039; confc: 629&lt;br /&gt;
2025.02.27 19:52:00.078 1: SolCast DEBUG&amp;gt; store &#039;NextHour04&#039; hod &#039;24&#039; confc: 660, confcEx: 660&lt;br /&gt;
2025.02.27 19:52:00.078 1: SolCast DEBUG&amp;gt; store circular/history hod &#039;24&#039; confc: 660&lt;br /&gt;
2025.02.27 19:52:00.079 1: SolCast DEBUG&amp;gt; store &#039;NextHour05&#039; hod &#039;01&#039; confc: 436, confcEx: 436&lt;br /&gt;
2025.02.27 19:52:00.079 1: SolCast DEBUG&amp;gt; store &#039;NextHour06&#039; hod &#039;02&#039; confc: 428, confcEx: 428&lt;br /&gt;
2025.02.27 19:52:00.079 1: SolCast DEBUG&amp;gt; store &#039;NextHour07&#039; hod &#039;03&#039; confc: 458, confcEx: 458&lt;br /&gt;
2025.02.27 19:52:00.079 1: SolCast DEBUG&amp;gt; store &#039;NextHour08&#039; hod &#039;04&#039; confc: 503, confcEx: 503&lt;br /&gt;
2025.02.27 19:52:00.080 1: SolCast DEBUG&amp;gt; store &#039;NextHour09&#039; hod &#039;05&#039; confc: 489, confcEx: 489&lt;br /&gt;
2025.02.27 19:52:00.080 1: SolCast DEBUG&amp;gt; store &#039;NextHour10&#039; hod &#039;06&#039; confc: 482, confcEx: 482&lt;br /&gt;
2025.02.27 19:52:00.080 1: SolCast DEBUG&amp;gt; store &#039;NextHour11&#039; hod &#039;07&#039; confc: 274, confcEx: 274&lt;br /&gt;
2025.02.27 19:52:00.081 1: SolCast DEBUG&amp;gt; store &#039;NextHour12&#039; hod &#039;08&#039; confc: 441, confcEx: 441&lt;br /&gt;
2025.02.27 19:52:00.081 1: SolCast DEBUG&amp;gt; store &#039;NextHour13&#039; hod &#039;09&#039; confc: 558, confcEx: 558&lt;br /&gt;
2025.02.27 19:52:00.081 1: SolCast DEBUG&amp;gt; store &#039;NextHour14&#039; hod &#039;10&#039; confc: 443, confcEx: 443&lt;br /&gt;
2025.02.27 19:52:00.081 1: SolCast DEBUG&amp;gt; store &#039;NextHour15&#039; hod &#039;11&#039; confc: 881, confcEx: 881&lt;br /&gt;
2025.02.27 19:52:00.082 1: SolCast DEBUG&amp;gt; store &#039;NextHour16&#039; hod &#039;12&#039; confc: 794, confcEx: 794&lt;br /&gt;
2025.02.27 19:52:00.082 1: SolCast DEBUG&amp;gt; store &#039;NextHour17&#039; hod &#039;13&#039; confc: 840, confcEx: 840&lt;br /&gt;
2025.02.27 19:52:00.082 1: SolCast DEBUG&amp;gt; store &#039;NextHour18&#039; hod &#039;14&#039; confc: 660, confcEx: 660&lt;br /&gt;
2025.02.27 19:52:00.083 1: SolCast DEBUG&amp;gt; store &#039;NextHour19&#039; hod &#039;15&#039; confc: 812, confcEx: 812&lt;br /&gt;
2025.02.27 19:52:00.083 1: SolCast DEBUG&amp;gt; store &#039;NextHour20&#039; hod &#039;16&#039; confc: 752, confcEx: 752&lt;br /&gt;
2025.02.27 19:52:00.083 1: SolCast DEBUG&amp;gt; store &#039;NextHour21&#039; hod &#039;17&#039; confc: 597, confcEx: 597&lt;br /&gt;
2025.02.27 19:52:00.083 1: SolCast DEBUG&amp;gt; store &#039;NextHour22&#039; hod &#039;18&#039; confc: 652, confcEx: 652&lt;br /&gt;
2025.02.27 19:52:00.084 1: SolCast DEBUG&amp;gt; store &#039;NextHour23&#039; hod &#039;19&#039; confc: 634, confcEx: 634&lt;br /&gt;
2025.02.27 19:52:00.084 1: SolCast DEBUG&amp;gt; store &#039;NextHour24&#039; hod &#039;20&#039; confc: 628, confcEx: 628&lt;br /&gt;
2025.02.27 19:52:00.084 1: SolCast DEBUG&amp;gt; store &#039;NextHour25&#039; hod &#039;21&#039; confc: 713, confcEx: 713&lt;br /&gt;
2025.02.27 19:52:00.084 1: SolCast DEBUG&amp;gt; store &#039;NextHour26&#039; hod &#039;22&#039; confc: 596, confcEx: 596&lt;br /&gt;
2025.02.27 19:52:00.085 1: SolCast DEBUG&amp;gt; store &#039;NextHour27&#039; hod &#039;23&#039; confc: 629, confcEx: 629&lt;br /&gt;
2025.02.27 19:52:00.085 1: SolCast DEBUG&amp;gt; store &#039;NextHour28&#039; hod &#039;24&#039; confc: 660, confcEx: 660&lt;br /&gt;
2025.02.27 19:52:00.086 1: SolCast DEBUG&amp;gt; consumption calculated - day: 27, hod: 20, con: 574 Wh&lt;br /&gt;
2025.02.27 19:52:00.086 1: SolCast DEBUG&amp;gt; write pvCircular consumption - hod: 99, todayConsumption: 11210 Wh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Ausgabe startet mit &#039;&#039;&amp;quot;Start Consumption forecast&#039;&#039;&amp;quot;, es werden die zu exkludierenden historischen Daten für jeden Consumer bzgl. jedem Tag (day) und Stunde des Tages (hod) getrennt registriert. Diese Werte werden durch die gespeicherte Einträge in der pvHistory (max. 31 historische Tage) geliefert. &lt;br /&gt;
&lt;br /&gt;
Im nächsten Abschnitt &#039;&#039;&amp;quot;Consumption forecast for the next Hours (new median)&amp;quot;&#039;&#039; werden die Prognosen für die nächsten Stunden kalkuliert und die zuvor registrierten Excludes angewendet. Die Prognose für den kommenden Tag ist im Abschnitt &#039;&#039;&amp;quot;Consumption forecast for the next day (new median)&amp;quot;&#039;&#039; protokolliert. Die Prognose für den kommenden Tag ist nicht einfach die Summe der Prognose der nächsten Stunden, denn in den kommenden Stunden werden auch die verfügbaren Planungen der Consumer einbezogen. Für den kommenden Tag können diese Werte unter Umständen nicht einbezogen werden da sie zum Zeitpunkt X noch nicht verfügbar sind.&lt;br /&gt;
&lt;br /&gt;
Im Abschnitt &#039;&#039;&amp;quot;Store Consumption forecast values (new median)&amp;quot;&#039;&#039; erscheinen die effektiv gespeicherten Werte. Diese sind die Grandlage für die grafische Darstellung sowie weiteren Kalkulationen.&lt;br /&gt;
&lt;br /&gt;
=== Wie kann eine fehlerhafte Verbrauchsprognose korrigiert werden? ===&lt;br /&gt;
Wie im vorherigen Abschnitt beschrieben, wird die Prognose aus den gespeicherten &amp;quot;con&amp;quot;-Werten der vergangenen Tage (bzw. Stunden) abgeleitet. &lt;br /&gt;
Grundsätzlich muß zunächst sichergestellt werden, dass die auszulesenden Readings der angegebenen Energiezähler sowie deren Einheiten! korrekt eingestellt sind. Das gilt ebenfalls für die registrierten Verbraucher im Modul.&lt;br /&gt;
&lt;br /&gt;
Für einen ersten Überblick bietet sich das Debug Attribut an zu setzen:&lt;br /&gt;
&lt;br /&gt;
 attr &amp;lt;Name&amp;gt; ctrlDebug consumption_long&lt;br /&gt;
&lt;br /&gt;
Beispielausgabe des Debug:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2024.09.21 13:28:27.041 1: SolCast DEBUG&amp;gt; ################### Consumption forecast for the next day ###################&lt;br /&gt;
2024.09.21 13:28:27.042 1: SolCast DEBUG&amp;gt; History Consumption day &amp;gt;01&amp;lt; considering possible exclusions: 14082&lt;br /&gt;
2024.09.21 13:28:27.042 1: SolCast DEBUG&amp;gt; History Consumption day &amp;gt;08&amp;lt; considering possible exclusions: 16446&lt;br /&gt;
2024.09.21 13:28:27.042 1: SolCast DEBUG&amp;gt; History Consumption day &amp;gt;15&amp;lt; considering possible exclusions: 14568&lt;br /&gt;
2024.09.21 13:28:27.043 1: SolCast DEBUG&amp;gt; History Consumption day &amp;gt;25&amp;lt; considering possible exclusions: 15522&lt;br /&gt;
2024.09.21 13:28:27.043 1: SolCast DEBUG&amp;gt; estimated Consumption for tomorrow: 15154, days for avg: 4&lt;br /&gt;
2024.09.21 13:28:27.043 1: SolCast DEBUG&amp;gt; ################### Consumption forecast for the next hours ###################&lt;br /&gt;
2024.09.21 13:28:27.044 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 07, hod: 14 -&amp;gt; 850 Wh&lt;br /&gt;
2024.09.21 13:28:27.044 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 14, hod: 14 -&amp;gt; 643 Wh&lt;br /&gt;
2024.09.21 13:28:27.044 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 24, hod: 14 -&amp;gt; 590 Wh&lt;br /&gt;
2024.09.21 13:28:27.045 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 31, hod: 14 -&amp;gt; 522 Wh&lt;br /&gt;
2024.09.21 13:28:27.045 1: SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 13:00:00, confc: 651, days for avg: 4, hist. consumption registered consumers: 560.97&lt;br /&gt;
2024.09.21 13:28:27.046 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 07, hod: 15 -&amp;gt; 720 Wh&lt;br /&gt;
2024.09.21 13:28:27.046 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 14, hod: 15 -&amp;gt; 743 Wh&lt;br /&gt;
2024.09.21 13:28:27.046 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 24, hod: 15 -&amp;gt; 897 Wh&lt;br /&gt;
2024.09.21 13:28:27.047 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 31, hod: 15 -&amp;gt; 557 Wh&lt;br /&gt;
2024.09.21 13:28:27.047 1: SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 14:00:00, confc: 729, days for avg: 4, hist. consumption registered consumers: 620.97&lt;br /&gt;
2024.09.21 13:28:27.047 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 07, hod: 16 -&amp;gt; 676 Wh&lt;br /&gt;
2024.09.21 13:28:27.048 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 14, hod: 16 -&amp;gt; 790 Wh&lt;br /&gt;
2024.09.21 13:28:27.048 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 24, hod: 16 -&amp;gt; 785 Wh&lt;br /&gt;
2024.09.21 13:28:27.048 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 31, hod: 16 -&amp;gt; 581 Wh&lt;br /&gt;
2024.09.21 13:28:27.049 1: SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 15:00:00, confc: 708, days for avg: 4, hist. consumption registered consumers: 647.29&lt;br /&gt;
2024.09.21 13:28:27.049 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 07, hod: 17 -&amp;gt; 622 Wh&lt;br /&gt;
2024.09.21 13:28:27.050 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 14, hod: 17 -&amp;gt; 639 Wh&lt;br /&gt;
2024.09.21 13:28:27.050 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 24, hod: 17 -&amp;gt; 646 Wh&lt;br /&gt;
2024.09.21 13:28:27.050 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 31, hod: 17 -&amp;gt; 572 Wh&lt;br /&gt;
2024.09.21 13:28:27.051 1: SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 16:00:00, confc: 619, days for avg: 4, hist. consumption registered consumers: 618.30&lt;br /&gt;
2024.09.21 13:28:27.051 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 07, hod: 18 -&amp;gt; 596 Wh&lt;br /&gt;
2024.09.21 13:28:27.051 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 14, hod: 18 -&amp;gt; 614 Wh&lt;br /&gt;
2024.09.21 13:28:27.052 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 24, hod: 18 -&amp;gt; 580 Wh&lt;br /&gt;
2024.09.21 13:28:27.052 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 31, hod: 18 -&amp;gt; 480 Wh&lt;br /&gt;
2024.09.21 13:28:27.052 1: SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 17:00:00, confc: 567, days for avg: 4, hist. consumption registered consumers: 583.41&lt;br /&gt;
2024.09.21 13:28:27.053 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 07, hod: 19 -&amp;gt; 615 Wh&lt;br /&gt;
2024.09.21 13:28:27.053 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 14, hod: 19 -&amp;gt; 1659 Wh&lt;br /&gt;
2024.09.21 13:28:27.053 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 24, hod: 19 -&amp;gt; 597 Wh&lt;br /&gt;
2024.09.21 13:28:27.054 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 31, hod: 19 -&amp;gt; 618 Wh&lt;br /&gt;
2024.09.21 13:28:27.054 1: SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 18:00:00, confc: 872, days for avg: 4, hist. consumption registered consumers: 636.97&lt;br /&gt;
2024.09.21 13:28:27.055 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 07, hod: 20 -&amp;gt; 611 Wh&lt;br /&gt;
2024.09.21 13:28:27.055 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 14, hod: 20 -&amp;gt; 1124 Wh&lt;br /&gt;
2024.09.21 13:28:27.055 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 24, hod: 20 -&amp;gt; 546 Wh&lt;br /&gt;
2024.09.21 13:28:27.056 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 31, hod: 20 -&amp;gt; 479 Wh&lt;br /&gt;
2024.09.21 13:28:27.056 1: SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 19:00:00, confc: 690, days for avg: 4, hist. consumption registered consumers: 578.87&lt;br /&gt;
2024.09.21 13:28:27.056 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 07, hod: 21 -&amp;gt; 1474 Wh&lt;br /&gt;
2024.09.21 13:28:27.057 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 14, hod: 21 -&amp;gt; 596 Wh&lt;br /&gt;
2024.09.21 13:28:27.057 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 24, hod: 21 -&amp;gt; 619 Wh&lt;br /&gt;
2024.09.21 13:28:27.057 1: SolCast DEBUG&amp;gt;     historical Consumption added for Sa -&amp;gt; date: 31, hod: 21 -&amp;gt; 433 Wh&lt;br /&gt;
2024.09.21 13:28:27.058 1: SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 20:00:00, confc: 780, days for avg: 4, hist. consumption registered consumers: 584.96&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Im ersten Teil wird die erwartete Consumption für den nächsten Tag unter Berücksichtigung möglicher Ausschlüsse von Verbrauchern ausgegeben. Ausschlüsse von Verbrauchern ergeben sich wenn in dem Verbraucherattribut der Schlüssel &#039;&#039;&#039;exconfc&#039;&#039;&#039; gesetzt ist. Er besagt, dass die Verbrauchswerte des Verbrauchers in eine zukünftige Prognose nicht eingehen sollen. Zum Beispiel wenn ein Verbraucher mit sehr hohen Verbrauchswerten nur sporadisch eingesetzt wird und dadurch die Prognose stark verfälschen würde. Es ist auch zu sehen, dass bedingt durch das Attribut &#039;&#039;&#039;affectConsForecastIdentWeekdays = 1&#039;&#039;&#039; nur die historischen Tage 01, 08, 15 und 25 berücksichtigt werden.&lt;br /&gt;
&lt;br /&gt;
Im zweiten Teil wird die berechnete Erwartung für die nächsten Stunden ausgegeben. &lt;br /&gt;
Zur Beschreibung der Fehlersuche gehen wir davon aus, dass die Prognose von 780 Wh am 2024-09-21 20:00:00 zu hoch erscheint weil am Tag &amp;quot;07&amp;quot; ein Wert von 1474 Wh gespeichert ist:&lt;br /&gt;
&lt;br /&gt;
 SolCast DEBUG&amp;gt; estimated Consumption for Sa -&amp;gt; starttime: 2024-09-21 20:00:00, confc: 780, days for avg: 4, hist. consumption registered consumers: 584.96&lt;br /&gt;
&lt;br /&gt;
Im nächsten Schritt geben wir die historischen Werte für den Tag 07 aus:&lt;br /&gt;
&lt;br /&gt;
 get &amp;lt;Name&amp;gt; pvHistory 07&lt;br /&gt;
&lt;br /&gt;
In dem Datensatz interessiert die Stunde 21. Sie resultiert aus der obigen Angabe &amp;quot;starttime: xxxx-xx-xx 20:00:00&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      21 =&amp;gt; etotal: 64354660, pvfc: 0, pvrl: 0, pvrlvd: 1, rad1h: -&lt;br /&gt;
            etotalp01: -, etotalp02: -, etotalp03: -&lt;br /&gt;
            pprl01: -, pprl02: -, pprl03: -&lt;br /&gt;
            confc: 496, con: 1474, gcons: 23, conprice: 0.2958&lt;br /&gt;
            gfeedin: 5, feedprice: 0.1269&lt;br /&gt;
            DoN: 0, sunaz: 289, sunalt: -8&lt;br /&gt;
            batintotal: 3064096.92456668, batouttotal: 2958021.37946804, batin: 0, batout: 1460&lt;br /&gt;
            wid: 101, wcc: 20, rr1c: 0.00, pvcorrf: 1.00/-temp: 26.00, &lt;br /&gt;
            csmt01: 64262.43, csme01: 28.8099999999977, minutescsm01: 25&lt;br /&gt;
            minutescsm02: 0&lt;br /&gt;
            csmt03: 0, csme03: 0, minutescsm03: 0&lt;br /&gt;
            csmt04: 1389417.4, csme04: 94.5, minutescsm04: 60&lt;br /&gt;
            csmt05: 0, csme05: 0, minutescsm05: 0&lt;br /&gt;
            csmt06: 290.15, csme06: 6.66999999999996, minutescsm06: 54&lt;br /&gt;
            csmt07: 0, csme07: 0, minutescsm07: 0&lt;br /&gt;
            csmt08: 32800, csme08: 0, minutescsm08: 0&lt;br /&gt;
            csmt09: 103987.8, csme09: 38, minutescsm09: 43&lt;br /&gt;
            csmt10: 9151.24999999783, csme10: 3.99999999997999, minutescsm10: 60&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dem Datensatz sind die Energieverbrauchsanteile der im Device registrierten Verbraucher mit dem Schlüssel &amp;quot;csmeXX&amp;quot; gespeichert. Dabei ist &amp;quot;XX&amp;quot; die Verbrauchernummer. In dem Beispiel existieren die Anteile csme01 und csme03 - csme10. Keiner dieser Verbraucher hat einen auffälligen Verbrauchswert gespeichert, d.h. die Messung der Verbraucher scheidet als Problemquelle aus. Wäre ein unverhältmismäßig hoher Verbrauch bei einem der Verbraucher feststellbar, müssten die Schlüssel &#039;&#039;&#039;pcurr&#039;&#039;&#039; und &#039;&#039;&#039;etotal&#039;&#039;&#039; im consumerXX-Attribut geprüft, sowie die Datenlieferung des Consumerdevices selbst geprüft werden.  &lt;br /&gt;
&lt;br /&gt;
Es wurde offensichtlich ein hoher Verbrauch von einem nicht registrierten Verbraucher verursacht oder durch die Übertragung eines Meßfehlers des Devices, angegeben im Attribut &#039;&#039;&#039;setupMeterDev&#039;&#039;&#039;, in das SolarForecast Device übertragen. Eine weitere Analyse ist an dieser Stelle nicht möglich.&lt;br /&gt;
&lt;br /&gt;
Zur Behebung des falschen Wertes kann ein Reset der gespeicherten Consumption der relevanten Stunde durchgeführt werden:&lt;br /&gt;
&lt;br /&gt;
 set &amp;lt;Name&amp;gt; reset consumptionHistory 07 21&lt;br /&gt;
&lt;br /&gt;
== Modulinterne Datenstrukturen ==&lt;br /&gt;
Das Modul speichert die verschiedenen historischen und aktuellen Daten wie PV-Prognose, Witterungsdaten (Regen, Bewölkung), Strahlungsdaten, Einspeisung, Bezug, reale Erzeugungsdaten und vieles mehr zur Laufzeit im Arbeitsspeicher. Dadurch ist die Verarbeitung dieser Daten im Vergleich zu Lesevorgängen aus dem Filesystem bzw. einer Datenbank sehr performant. Allerdings würden diese Daten bei einem FHEM Absturz bzw. bei einem regulären Shutdown verlorengehen.&lt;br /&gt;
&lt;br /&gt;
Deswegen werden die relevanten Daten regelmäßig in Dateien gesichert und bei Bedarf wiederhergestellt. Diese Dateien befinden sich im Verzeichnis &#039;&#039;&#039;../fhem/FHEM/FhemUtils&#039;&#039;&#039; und heißen &#039;&#039;&#039;.*_SolarForecast_.*&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Die Daten werden in jedem Zyklus der Zentralschleife (CentralTask) gesammelt bzw. berechnet. Der Zyklus wird mit dem Attribut &#039;&#039;&#039;plantControl-&amp;gt;cycleInterval&#039;&#039;&#039; eingestellt.&lt;br /&gt;
&lt;br /&gt;
Zur Laufzeit können diese Daten über get-Kommandos abgerufen werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get ... pvHistory [Tag]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der pvHistory Datenspeicher enthält die vergangenen Kennwerte der letzten 31 Tage mit einer Genauigkeit von einer Stunde. Es werden die Prognosewerte für Erzeugung und Verbrauch, die realen Werte für Erzeugung und Verbrauch sowie ebenso die Laufzeiten jedes einzelnen Verbrauchers (Consumer) festgehalten. Für jeden Tag gibt es die Stunde &amp;quot;99&amp;quot; die eine Summierung der Stundenwerte des Tages bzw. Sonderschlüssel enthält.&lt;br /&gt;
&lt;br /&gt;
Bei Bedarf lässt sich mit dem Befehl &amp;quot;set ... reset pvHistory&amp;quot; der gesamte Inhalt des Speichers löschen (nicht empfohlen). Selektiver funktioniert die Datenlöschung mit &amp;quot;set ... reset pvHistory &amp;lt;Tag&amp;gt;&amp;quot; (Daten eines bestimmten Tages löschen) bzw. &amp;quot;set ... reset pvHistory &amp;lt;Tag&amp;gt; &amp;lt;Stunde&amp;gt;&amp;quot; (Daten der Stunde  eines bestimmten Tages löschen). Die letzten beiden Kommandos müssen über die Kommandozeile im Browser ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get ... pvCircular &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser Ringspeicher enthält Daten der letzten 24 Stunden sowie fortwährend berechnete Werte wie Korrekturfaktoren der PV-Erzeugung und Prognosequalität.&lt;br /&gt;
Neue Stundenwerte des Tages überschreiben die gespeicherten Schüssel des vorangegangenen Tages sofern es sich nicht um fortwährend berechnete Werte handelt. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get ... nextHours&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Speicher enthält sämtliche Prognosewerte der kommenden Stunden beginnend mit der aktuellen Stunde.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get ... radiationApiData&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abhängig von der ausgewählten Strahlungsdaten API enthält dieser Speicher die von der API gelieferten Rohdaten sowie evtl. weitere für die Funktion benötigte Daten wie API-Keys, Response Messages, Anzahl der API Abruf und weitere.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get ... valCurrent&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Struktur wird nicht regelmäßig im Filesystem gespeichert und enthält immer aktuell gesammelte oder berechnete/gelieferte Daten wie z.B. die Autarkierate, letze Laufzeit der Zentralschleife (CentralTask), Zeit des Sonnenauf- und untergangs sowie weitere Betriebsdaten. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
get ... valConsumerMaster [XX]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zeigt die Daten der aktuell im SolarForecast Device registrierten Verbraucher. Mit der Nummer &#039;&#039;&#039;XX&#039;&#039;&#039; kann ein bestimmter Verbraucher angesprungen werden. Die Nummer korrespondiert mit dem entsprechenden &#039;&#039;&#039;consumerXX&#039;&#039;&#039; Attribut.&lt;br /&gt;
Neben anderen Betriebs- und Energiedaten werden insbesondere folgende Daten ermittelt, die für die zukünftige Planung der Verbraucherschaltzeiten relevant sind:&lt;br /&gt;
&lt;br /&gt;
* epieces - prognostizierte Energiescheiben pro Betriebsstunde &lt;br /&gt;
&lt;br /&gt;
Ausgehend von gespeicherten historischen Verbrauchsdaten des Verbrauchers wird der Energieverbrauch des Verbrauchers in seiner ersten Betriebsstunde prognostiziert, was insbesondere für die Planung der optimalen Startzeit des Verbrauchers in Bezug zu der prognostizierten PV Energie und den Verbrauchswerten weiterer vorhandener bzw. zu planender Verbraucher Berücksichtigung findet. Weitere Informationen zur Verbrauchersteuerung in diesem  [[#Verbrauchersteuerung_-_Registrieren_und_visualisieren_von_Verbrauchern|Abschnitt]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Schnittstelle für Modulautoren bzw. eigenen Code ===&lt;br /&gt;
&lt;br /&gt;
Die im vorherigen Artikel beschriebenen internen Datenstrukturen können durch externen Code abgefragt und ausgewertet werden.&lt;br /&gt;
Dazu stehen die folgenden Funktionen zur Verfügung. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]BatteryVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;Bn&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... valBattery&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]CurrentVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... valCurrent&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]InverterVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;Nr&amp;gt;&#039;, &amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... valInverter&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]ProducerVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;Nr&amp;gt;&#039;, &amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... valProducer&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]HistoryVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;Day&amp;gt;&#039;, &#039;&amp;lt;HoD&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... pvHistory&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]CircularVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;HoD&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... pvCircular&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]NexthoursVal (&#039;&amp;lt;Name&amp;gt;&#039;, &amp;lt;nhr&amp;gt;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... nextHours&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]ConsumerVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;Nr&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... valConsumerMaster&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]RadiationAPIVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;String&amp;gt;&#039;, &#039;&amp;lt;DateTime&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... radiationApiData&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]WeatherAPIVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;APIname&amp;gt;&#039;, &#039;&amp;lt;TimeCode&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... weatherApiData&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]StatusAPIVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;APIname&amp;gt;&#039;, &#039;&amp;lt;QTag&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... statusApiData&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
::* [FHEM::SolarForecast::]StringVal (&#039;&amp;lt;Name&amp;gt;&#039;, &#039;&amp;lt;String&amp;gt;&#039;, &#039;&amp;lt;Key&amp;gt;&#039;, &amp;lt;default&amp;gt;) &amp;lt;br&amp;gt; entspricht der Abfrage mit &amp;quot;get ... valStrings&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Angabe des Packages (FHEM::SolarForecast::) ist nur notwendig wenn die Funktionen von außerhalb des SolarForecast-Devices aufgerufen werden. Innerhalb des Devices (Code-Ausführung im Attribut &#039;&#039;ctrlUserExitFn&#039;&#039;) kann darauf verzichtet werden.&lt;br /&gt;
&lt;br /&gt;
Die Bedeutung der Platzhalter sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     &amp;lt;Name&amp;gt;     - Name des SolarForecast Devices&lt;br /&gt;
     &amp;lt;Day&amp;gt;      - abzufragender Tag (01 - 31)&lt;br /&gt;
     &amp;lt;Bn&amp;gt;       - Batterie Nummer (01, 02, ...)&lt;br /&gt;
     &amp;lt;DateTime&amp;gt; - Startzeit der Form YYYY-MM-DD hh:00:00&lt;br /&gt;
     &amp;lt;TimeCode&amp;gt; - Zeitwert der Form fcX_XX (z.B. fc1_19)&lt;br /&gt;
     &amp;lt;APIname&amp;gt;  - Hauptname der API gemäß setupWeatherDev1 (z.B. OpenMeteo)&lt;br /&gt;
     &amp;lt;String&amp;gt;   - Name des PV-Modulstrings wie im Attribut &#039;setupInverterStrings&#039;&lt;br /&gt;
     &amp;lt;QTag&amp;gt;     - Question Tag, z.B. &#039;?All&#039;&lt;br /&gt;
     &amp;lt;Nr&amp;gt;       - laufende Nummer des Verbrauchers / Gerätes (01, 02, 03, 04,...)&lt;br /&gt;
     &amp;lt;HoD&amp;gt;      - abzufragende Stunde (01 - 24, 99)&lt;br /&gt;
     &amp;lt;nhr&amp;gt;      - nächste Stunde (NextHour00, NextHour01,...)&lt;br /&gt;
     &amp;lt;Key&amp;gt;      - der abzufragende Schlüssel &lt;br /&gt;
     &amp;lt;default&amp;gt;  - def default-Rückgabewert&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
==== Beispiel Auslesen der Batterieladestrategie und Anlegen eines Readings ====&lt;br /&gt;
&lt;br /&gt;
Als Praxisbeispiel soll die aktuelle gesetzte Ladestrategie für Batterie &amp;quot;01&amp;quot; ausgelesen und mit dem Wert ein Reading im SolarForecast Device angelegt werden. &amp;lt;br&amp;gt;&lt;br /&gt;
Dazu wird der folgende Code im Attribut &#039;&#039;ctrlUserExitFn&#039;&#039; hinterlegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  my $wert = NexthoursVal ($name, &#039;NextHour00&#039;, &#039;strategybat01&#039;, &#039;loadRelease&#039;);&lt;br /&gt;
  storeReading (&#039;Ladestrategie&#039;, $wert);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei jedem Zyklus des SF-Devices wird der aktuelle Wert von &#039;&#039;strategybat01&#039;&#039; gelesen und im Reading &#039;&#039;Ladestrategie&#039;&#039; gespeichert. Die Paket-Definition &#039;&#039;FHEM::SolarForecast::&#039;&#039; braucht nicht verwendet werden, da der Aufruf im Attribut ctrlUserExitFn innerhalb des SolarForecast-Paketes erfolgt.&lt;br /&gt;
&lt;br /&gt;
Für andere Batterienummern ist der NexthoursVal-Schlüssel &#039;&#039;strategybat01&#039;&#039; entsprechend anzupassen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Auswerten von internen Arrays ===&lt;br /&gt;
&lt;br /&gt;
Die meisten der internen Datenstrukturen liefern bei der Abfrage einen einfachen String oder numerischen Wert der ausgewertet werden kann. Es gibt allerdings auch intern gespeicherte Wertelisten. Werden die Listenstrukturen mit den oben beschriebenen Befehlen abgefragt, wird eine Referenz auf die Liste geliefert sofern sie vorhanden ist.&lt;br /&gt;
&lt;br /&gt;
Um auf die Werte einer solchen Liste zuzugreifen, ist am Beispiel des Arrays &#039;&#039;surplusslidereg&#039;&#039; aus dem Current-Speicher (get ... valCurrent) dargestellt: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
my $splref = CurrentVal ($name, &#039;surplusslidereg&#039;, &#039;.&#039;);&lt;br /&gt;
my $spser  = ref $splref eq &#039;ARRAY&#039; ? join &#039; &#039;, @{$splref} : undef;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Variable $spser enthält bei Vorhandensein der Liste einen String, der die Listenelemente von &#039;surplusslidereg&#039; durch Leerzeichen getrennt enthält.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Backup und Wiederherstellung der Moduldaten ==&lt;br /&gt;
Neben der Datenhaltung in den Attributen und Readings des SolarForecast Devices, erzeugt jedes SolarForecast Device Dateien im Verzeichnis  &#039;&#039;&#039;../fhem/FHEM/FhemUtils&#039;&#039;&#039; mit folgenden Inhalten:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PVH_SolarForecast_&amp;lt;name&amp;gt;        - PV History&lt;br /&gt;
PVC_SolarForecast_&amp;lt;name&amp;gt;        - PV Circular&lt;br /&gt;
PVCfg_SolarForecast_&amp;lt;name&amp;gt;      - PV Anlagenkonfiguration&lt;br /&gt;
PVCsm_SolarForecast_&amp;lt;name&amp;gt;      - Consumer Status&lt;br /&gt;
ScApi_SolarForecast_&amp;lt;name&amp;gt;      - Strahlungswerte aus verwendeter API&lt;br /&gt;
StatApi_SolarForecast_&amp;lt;name&amp;gt;    - Statusdaten der verwendeten API&#039;s&lt;br /&gt;
WeatherApi_SolarForecast_&amp;lt;name&amp;gt; - Wetterdaten der gewählten Wetter-API (außer DWD-Device)&lt;br /&gt;
AItra_SolarForecast_&amp;lt;name&amp;gt;      - KI Entscheidungsquelle (trainierte Daten)&lt;br /&gt;
AIraw_SolarForecast_&amp;lt;name&amp;gt;      - KI Input Daten = Raw Trainigsdaten&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Je nach Ausprägung und eingesetzten Features des SolarForecast Devices sind alle oder nur ein Teil dieser Dateien vorhanden. Dabei ist &#039;&#039;&#039;&amp;lt;name&amp;gt;&#039;&#039;&#039; der Name des SolarForecast Devices.&lt;br /&gt;
&lt;br /&gt;
Bei einem Filebackup des FHEM-Servers ist darauf zu achten, dass diese Dateien in dem Backup enthalten sind. Der Inhalt der Dateien ist nicht statisch, sondern kann sich dynamisch aktualisieren. Um im Bedarfsfall einen aktuellen Datenstand zu haben, ist mindestens ein tägliches Backup dieser Dateien empfehlenswert. Eventuell auch öfter mit Hilfe einer Versionierung welche z.B. immer die letzten 3 Versionen aufbewahrt.&lt;br /&gt;
&lt;br /&gt;
Beim Start von FHEM werden bestimmte Dateien automatisch gelesen und in das SolarForecast Device importiert. &lt;br /&gt;
Der Vorgang ist mit verbose=3 im Log protokolliert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2023.10.02 09:57:07.967 3: SolCast6 - cached data &amp;quot;pvHistory&amp;quot; restored&lt;br /&gt;
2023.10.02 09:57:07.969 3: SolCast6 - cached data &amp;quot;pvCircular&amp;quot; restored&lt;br /&gt;
2023.10.02 09:57:07.970 3: SolCast6 - cached data &amp;quot;consumerMaster&amp;quot; restored&lt;br /&gt;
2023.10.02 09:57:07.970 3: SolCast6 - cached data &amp;quot;radiationApiData&amp;quot; restored&lt;br /&gt;
2023.10.02 09:57:07.970 3: SolCast6 - cached data &amp;quot;statusApiData&amp;quot; restored&lt;br /&gt;
2023.10.02 09:57:07.970 3: SolCast6 - cached data &amp;quot;weatherApiData&amp;quot; restored&lt;br /&gt;
2023.10.02 09:57:07.974 3: SolCast6 - cached data &amp;quot;aiTrainedData&amp;quot; restored&lt;br /&gt;
2023.10.02 09:57:07.976 3: SolCast6 - cached data &amp;quot;aiRawData&amp;quot; restored&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Bestimmte Betriebsdaten, wie z.B. die witterungsabhängigen Korrekturfaktoren (circular) oder die KI-Daten (aitrained/airaw), bauen sich über die Zeit immer granularer auf und stellen einen wertvollen Datenbestand dar.&lt;br /&gt;
&lt;br /&gt;
Wird das SolarForecast Device gelöscht und anschließend wieder neu mit dem gleichen Namen definiert, können die bisher verfügbaren Daten sehr einfach wiederhergestellt werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* 1. definieren des neuen SolarForecast Devices, d.h. ein einfaches &amp;quot;define &amp;lt;Name&amp;gt; SolarForecast&amp;quot;, und sichern der FHEM Konfiguration (der Konfigurationsdialog muß nicht durchgeführt werden)&lt;br /&gt;
* 2. FHEM stoppen&lt;br /&gt;
* 3. wiederherstellen der oben beschriebenen Dateien aus einem Backup in das Verzeichnis ../fhem/FHEM/FhemUtils (Überschreiben evtl. vorhandener Dateien)&lt;br /&gt;
* 4. Anpassen der Dateieigentümer und Berechtigungen: chown fhem:dialout /opt/fhem/FHEM/FhemUtils/*,  chmod 774 /opt/fhem/FHEM/FhemUtils/*&lt;br /&gt;
* 5. starten von FHEM (bestimmte Daten werden automatisch importiert)&lt;br /&gt;
* 6. mit dem Befehl &amp;quot;set &amp;lt;name&amp;gt; plantConfiguration restore&amp;quot; die Anlagenkonfiguration wiederherstellen&lt;br /&gt;
* 7. FHEM Konfiguration sichern und restarten&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
Das zuvor beschriebene Verfahren kann auch angewendet werden um das SolarForcast Device von einer FHEM Installation in eine andere Installation umzuziehen. In diesem Fall ist aber besonders darauf zu achten, vorab alle in den SF-Attributen referenzierten Devices wie Inverter, Meter oder DWD-Devices mit den zutreffenden Namen in der neuen Installation anzulegen.&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Soll das neue SolarForecast Device mit einem anderen Namen oder weitere SolarForecast Devices erstellt werden, welche die Betriebsdaten des primären Devices verwenden sollen, ist wie folgt vorzugehen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* 1. definieren des neuen SolarForecast Devices, d.h. ein einfaches &amp;quot;define &amp;lt;Name&amp;gt; SolarForecast&amp;quot;, und sichern der FHEM Konfiguration (der Konfigurationsdialog muß nicht durchgeführt werden)&lt;br /&gt;
* 2. FHEM stoppen&lt;br /&gt;
* 3. wiederherstellen der oben beschriebenen Dateien aus einem Backup in das Verzeichnis ../fhem/FHEM/FhemUtils (Überschreiben evtl. vorhandener Dateien)&lt;br /&gt;
* 4. umbenenen der Dateien, wobei &amp;lt;name&amp;gt; durch den Namen des neuen SolarForecast Devices ersetzt wird&lt;br /&gt;
* 5. Anpassen der Dateieigentümer und Berechtigungen: chown fhem:dialout /opt/fhem/FHEM/FhemUtils/*,  chmod 774 /opt/fhem/FHEM/FhemUtils/*&lt;br /&gt;
* 6. starten von FHEM (bestimmte Daten werden automatisch importiert)&lt;br /&gt;
* 7. mit dem Befehl &amp;quot;set &amp;lt;name&amp;gt; plantConfiguration restore&amp;quot; die Anlagenkonfiguration wiederherstellen&lt;br /&gt;
* 8. FHEM Konfiguration sichern und restarten&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wenn sich die SolarForecast Devices hinsichtlich ihres Models oder der integrierten Consumer unterscheiden, sind die Dateien &amp;quot;PVCsm_SolarForecast_&amp;lt;name&amp;gt; &amp;quot; , &amp;quot;ScApi_SolarForecast_&amp;lt;name&amp;gt;&amp;quot; von dem Verfahren auszuschließen.&lt;br /&gt;
&lt;br /&gt;
== Batterieintegration und -steuerung ==&lt;br /&gt;
&lt;br /&gt;
=== Wie eine Batterie eingebunden wird ===&lt;br /&gt;
Wie immer muß die Batterieanlage zunächst mit einem für die Batterie spezifischen FHEM-Device in FHEM definiert sein.&lt;br /&gt;
In diesem Device werden Readings erwartet die folgende Werte bereitstellen:&lt;br /&gt;
&lt;br /&gt;
::* ein Reading welches die aktuelle Batterieladeleistung liefert&lt;br /&gt;
&lt;br /&gt;
::* ein Reading welches die aktuelle Batterieentladeleistung liefert&lt;br /&gt;
&lt;br /&gt;
::* ein Reading welches die installierte Batteriekapazität liefert (&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; dieser Schlüssel ist für die Aktivierung des Batterie SOC-Managements und die grafische Anzeige des SOC wesentlich!)&lt;br /&gt;
&lt;br /&gt;
::* ein Reading welches die bis dato vorliegende Lade-Energiemenge als fortlaufenden Zähler liefert (optional)&lt;br /&gt;
&lt;br /&gt;
::* ein Reading welches die bis dato vorliegende Entlade-Energiemenge als fortlaufenden Zähler liefert (optional)&lt;br /&gt;
&lt;br /&gt;
::* Reading welches den aktuellen Ladezustand (SOC in Prozent) liefert (optional)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Batterie, bzw. das Batteriedevice wird mit dem Attribut &#039;&#039;&#039;setupBatteryDev&#039;&#039;&#039; im SolarForecast Device registriert. Die Syntax des Attributes ist in  der Online-Hilfe zum Attribut umfassend erläutert. &lt;br /&gt;
Auch wenn manche Readings optional sind, wird empfohlen alle Werte bereitzustellen um alle Batterie-Funktionen im Modul nutzen zu können. Durch die Weiterentwicklung des Moduls können optionale Readings später auch obligatorisch werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Visualisierung der Batterie in der Balkengrafik ====&lt;br /&gt;
&lt;br /&gt;
Das Attribut &#039;&#039;&#039;setupBatteryDevXX&#039;&#039;&#039; verfügt über den Schlüssel &#039;&#039;&#039;show&#039;&#039;&#039;, mit dem die Einblendung des bzw. der Batteriesymbole in den Ebenen der Balkengrafik gesteuert werden kann:&lt;br /&gt;
[[Datei:Screenshot 2025-02-23 192002.png|right|thumb|400px|Einblendung der Batterieleiste und Darstellung des Batterie-SoC als Balkencontent in der zweiten Ebene]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* 0 - keine Anzeige des Gerätes (default)&lt;br /&gt;
* 1 - Anzeige des Gerätes in der Balkengrafik Ebene 1&lt;br /&gt;
* 2 - Anzeige des Gerätes in der Balkengrafik Ebene 2 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Symbole und die Farbgebung der Batterie(n) können mittels des Schlüssels &#039;&#039;&#039;icon&#039;&#039;&#039; verändert werden. Die Online-Hilfe gibt weitere Informationen zur Ausgestaltung. &lt;br /&gt;
&lt;br /&gt;
In der Batteriedarstellung wird das Symbol der aktuellen Stunde hervorgehoben. Ein Mouse-Over zeigt wesentliche Betriebszustände der Batterie. Die Batteriesymbole vergangener Stunden zeigen mit einem Mouse-Over die erreichten SoC (State of Charge) Stände. Die kommenden Stunden visualisieren die folgenden Prognosen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* der prognostizierte SoC unter Berücksichtigung der PV-Prognose und der [[#Aktivierung_des_Batterie_SOC-Managements|Batteriesteuerung]] sofern sie im Modul aktiviert ist.&lt;br /&gt;
* eine Freigabe bzw. Empfehlung der Batterieladung zur [[#Unterstützung_eines_netzdienlichen_Verhaltens|Unterstützung eines netzdienlichen Verhaltens]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Neben der Einblendung der Batterieleiste über der Balkengrafik selbst können bestimmte Batteriekennwerte als Balkeninhalt dargestellt werden. Die Auswahl erfolgt über das Attribut &#039;&#039;&#039;graphicBeamXContent&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Aktivierung des Batterie SOC- und Lade-Managements ===&lt;br /&gt;
Das integrierte SOC- und Lade-Management verfolgt insbesondere diese Ziele: &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* den Ladezustand der Batterie(n) zu optmimieren, was besonders in den Monaten mit geringerer PV-Erzeugung einen wertvollen Beitrag zur Erhöhung der Autarkierate und Batteriepflege leisten kann&lt;br /&gt;
 &lt;br /&gt;
* eine PV-Prognose und Verbrauch optimierte Beladungssteuerung, die in den Monaten mit hoher PV-Erzeugung hilft eine evtl. Abregelung der Anlage zu verhindern, die Batterie ausgewogen über den Tag zu laden und ein netzdienliches Verhalten zu unterstützen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Diese Aspekte der Batteriesteuerung werden in den folgenden Abschnitten beschrieben. Sind mehrere Batterien installiert, kann die Steuerung getrennt für jede einzelne Batterie aktiviert und eingestellt werden. Dabei ist zu beachten, dass vom Modul nur Signale und Readings bereitgestellt werden, die durch eine entsprechende Routine ausgewertet werden können um spezifische Befehle zur Umsetzung der Steuerung an das Batteriesystem zu senden. &lt;br /&gt;
 &lt;br /&gt;
Für eine Aktivierung des Managements muß zunächst das Batterie-Device wie im vorigen Abschnitt im SolarForecast Device registriert werden.&lt;br /&gt;
Das Attribut &#039;&#039;&#039;ctrlBatSocManagementXX&#039;&#039;&#039; aktiviert die im Modul integrierte SOC- und Lade-Management Routine: &lt;br /&gt;
&lt;br /&gt;
 attr &amp;lt;Device&amp;gt; ctrlBatSocManagementXX lowSoc=&amp;lt;Wert&amp;gt; upSoC=&amp;lt;Wert&amp;gt; [maxSoC=&amp;lt;Wert&amp;gt;] [careCycle=&amp;lt;Wert&amp;gt;] [loadAbort=&amp;lt;SoC1&amp;gt;:&amp;lt;MinPwr&amp;gt;:&amp;lt;SoC2&amp;gt;] [safetyMargin=&amp;lt;Wert&amp;gt;[:&amp;lt;Wert&amp;gt;]] ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die einzelnen Schlüssel werden der Kalkulationsroutine als Steuerungsparameter übergeben und bedeuten:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;lowSoc:unterer Mindest-SoC, die Batterie wird nicht tiefer als dieser Wert entladen (muß &amp;gt; 0 sein). Dieser Wert kann zum Beispiel dazu dienen, stets eine Mindestenergiemenge für den Fall eines Stromausfalls im öffentlichen Netz in den Batterien vorzuhalten.&lt;br /&gt;
&lt;br /&gt;
;upSoC:oberer Mindest-SoC, Der übliche Wert des optimalen SoC bewegt sich in Perioden mit hohen PV-Überschuß tendenziell zwischen &#039;lowSoC&#039; und &#039;upSoC&#039;, in Perioden mit geringer PV-Ausbeute bzw. geringem PV Energieüberschuß bewegt sich der optimale SoC tendenziell zwischen &#039;upSoC&#039; und &#039;maxSoC&#039;. Die Einstellung von upSoC=50 für ein ausgewogenes Verhalten kann als guter Startwert dienen.&lt;br /&gt;
&lt;br /&gt;
;maxSoC:maximaler Mindest-SoC, d.h. der SoC Wert der mindestens im Abstand von &#039;careCycle&#039; Tagen erreicht werden muß um den Ladungsausgleich im Speicherverbund auszuführen. Die Angabe ist optional (muß &amp;lt;= 100 sein, default: 95)&lt;br /&gt;
&lt;br /&gt;
;stepSoC: Optionale Schrittweite zur optimalen SoC-Berechnung (Battery_OptimumTargetSoC_XX) in %. Mit der Angabe &#039;stepSoC=0&#039; wird das SoC-Management deaktiviert und Battery_OptimumTargetSoC_XX auf den Wert &#039;lowSoC&#039; gesetzt. &#039;&#039;&#039;Hinweis:&#039;&#039;&#039; Die Beziehung &#039;careCycle * stepSoC = 100&#039; sollte eingehalten werden! Wert: 0..5, default: 5 &lt;br /&gt;
&lt;br /&gt;
;careCycle:maximaler Abstand in Tagen, der zwischen zwei Ladungszuständen von mindestens &#039;maxSoC&#039; auftreten darf. Die Angabe ist optional. (default: 20)&lt;br /&gt;
&lt;br /&gt;
;lcSlot: Es wird ein tägliches Zeitfenster festgelegt, in dem die Ladesteuerung des Moduls für diese Batterie aktiv sein soll. Außerhalb des Zeitfensters wird die Batterieladung mit voller Leistung freigegeben. Das SoC-Management der Batterie ist davon nicht betroffen. Wert: &amp;lt;hh:mm&amp;gt;-&amp;lt;hh:mm&amp;gt;, default: ganztägig&lt;br /&gt;
&lt;br /&gt;
;loadAbort: definiert eine vom Anwender gewünnschte Bedingung für einen generellen Ladeabbruch und Wiederfreigabe der Lademöglichkeit. Die Abbruchbedingung ist erfüllt, wenn der angegebene SoC1 (%) erreicht bzw. überschritten ist &#039;&#039;&#039;UND&#039;&#039;&#039; die angegebene Ladeleistung &amp;lt;MinPwr&amp;gt; (W) unterschritten wurde -&amp;gt; Reading Battery_ChargeAbort_XX=1. Fällt der aktuelle SoC wieder unter den SoC2, wird Battery_ChargeAbort_XX=0 gesetzt. Ist SoC2 nicht angegeben, gilt SoC2=SoC1.  &lt;br /&gt;
&lt;br /&gt;
;loadStrategy: Abhängig von der gewählten Ladestrategie wird die Prognose der Batterieladung und ggf. die Generierung der Steuerreadings beeinflusst. Die Angabe ist optional. Wert: loadRelease | optPower | smartPower, default: loadRelease&lt;br /&gt;
	&lt;br /&gt;
;loadTarget: Optionaler Ziel-SoC in % für die Berechnung der Ladefreigabe bzw. der optimalen Ladeleistung. Der Zielwert ist eine kalkulatorische Rechengröße. Der reale SoC kann situativ in Grenzen über- oder unterschritten werden. Der höhere Wert aus Reading Battery_OptimumTargetSoC_XX und &#039;loadTarget&#039; hat für die Berechnung Vorrang. Wert: 0..100, default: 100 &lt;br /&gt;
&lt;br /&gt;
;safetyMargin: Bei der Berechnung der Ladefreigabe und optimierten Ladeleistung werden Sicherheitszuschläge auf den prognostizierten Ladungsbedarf berücksichtigt. Abweichend vom Default können mit diesem Parameter individuelle Sicherheitszuschläge getrennt für die Berechnung der Ladefreigabe und optimierten Ladeleistung angegeben werden. Der erste Wert ist der Zuschlag bei der Berechnung der Ladefreigabe, der zweite Wert der Zuschlag bei der Berechnung der optimierten Ladeleistung. Beide Angaben sind Prozentwerte von 0..100.&lt;br /&gt;
&lt;br /&gt;
;weightOwnUse: Optionale Gewichtung der stündlichen Verbrauchsprognose als zusätzlich verwendbaren Anteil zur Batterieladung in %. Technisch wird der verfügbare PV-Überschuß zur Berechnung der optimierten Ladeleistung erhöht indem der kalkulierte Verbrauch um den angegebenen Prozentsatz gesenkt wird. Wert: 0..100 default: 0 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Alle SoC-Werte sind ganze Zahlen in %. Dabei gilt: &lt;br /&gt;
&lt;br /&gt;
 lowSoc &amp;lt; upSoC &amp;lt; maxSoC&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====  Erläuterung der Batterie-Steuerungsreadings ==== &lt;br /&gt;
&lt;br /&gt;
Für die Steuerung des Batteriesystems gibt es im SolarForecast einen Satz an Readings, die unterschiedliche Zustände signalisieren und für entsprechende Steuerungsaufgaben bezüglich der Batteriesysteme eingesetzt werden können. In den nachfolgenden Abschnitten werden diese Zusammenhänge noch dataillierter beschrieben.&lt;br /&gt;
&lt;br /&gt;
;Hinweis: SolarForecast stellt lediglich Steuerungsinformationen zur Verfügung. Die Umsetzung des Eingriffs in das Batteriesystem erfolgt durch den Anwender selbst über entsprechende Skripte. In den thematischen Abschnitten werden Beispiele für solche Steuerungsskripte vorgestellt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Alle erstellten Readings gelten für ein Batteriesystem, es können mehrere Batteriesysteme in SolarForecast integriert sein. Die Endung &#039;&#039;XX&#039;&#039; der Readings beschreibt die Nummer des Batteriesystems und korrespondiert mit der Endung des entsprechenden Attributes &#039;&#039;ctrlBatSocManagementXX&#039;&#039; bzw. &#039;&#039;setupBatteryDevXX&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Battery_OptimumTargetSoC_XX: Mit den im Attribut &#039;&#039;ctrlBatSocManagementXX&#039;&#039; hinterlegten Parametern unter Berücksichtigung von PV- und Verbrauchsprognosen wird ein optimaler SOC-Wert errechnet. Dieser Wert dient zur Einstellung des unteren SOC des Batteriesystems.&lt;br /&gt;
&lt;br /&gt;
;Battery_ChargeRequest_XX: Dieses Reading signalisiert mit 1 eine Anforderung zur Zwangsladung der Batterie um die Batterie vor Schäden zu schützen (wenn der SOC unter ctrlBatSocManagementXX-&amp;gt;lowSoc gefallen ist) oder wenn der aktuelle SOC unter den berechneten Mindest-SOC gefallen ist.&lt;br /&gt;
&lt;br /&gt;
;Battery_ChargeUnrestricted_XX: Dieses Reading kann den Wert &#039;&#039;&#039;0&#039;&#039;&#039; oder &#039;&#039;&#039;1&#039;&#039;&#039; einnehmen. Ist der Wert 0, sollte die Batterie nur geladen werden, wenn der im Attribut &#039;&#039;plantControl-&amp;gt;feedinPowerLimit&#039;&#039; definierte Wert überschritten wird. Ist der Wert 1, sollte die Batterie mit der vollen zur Verfügung stehenden Ladeleistung angesteuert werden, um wie prognostiziert am Ende des Sonnentages die volle bzw. maximal mögliche Ladung zu erhalten. Der Fokus der Steuerung mit diesem Reading ist es, die Speicherkapazität der Batterie optimal zur Verhinderung der Überschreitung von Einspeiselimits einzusetzen und dennoch einen maximal möglichen SoC am Tagesende zu erreichen. (siehe das beschriebene [[SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#PV-Prognose_und_Verbrauch_optimierte_Beladungssteuerung_unter_Berücksichtigung_einer_Wirkleistungsbegrenzung|Nutzungsbeispiel]])&lt;br /&gt;
&lt;br /&gt;
;Battery_ChargeOptTargetPower_XX: Dieses Reading enthält einen Richtwert für die optimierte Ladeleistung für jede Batterie. Die Aufladung wird prognosegeführt über den gesamten Tag verteilt, wobei der Fokus auf eine kontinuierliche, aber möglichst niederige Ladeleistung gelegt wird. (siehe diesen [[SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#leistungsoptimierte_Beladungssteuerung_mit_dem_Fokus_geringe_Verlustleistung_und_Verschleiß|Abschnitt]])  &lt;br /&gt;
&lt;br /&gt;
;Battery_ChargeAbort_XX: Im Attribut &#039;&#039;ctrlBatSocManagementXX&#039;&#039; kann mit dem Schlüssel &#039;&#039;loadAbort&#039;&#039; eine Bedingung definiert werden, bei deren Eintritt die Ladung der Batterie abgebrochen werden soll. In diesem Fall wird &#039;&#039;Battery_ChargeAbort_XX=1&#039;&#039; gesetzt. Mit einem entsprechenden Befehl kann der Anwender das Batteriesystem anweisen den Ladevorgang abzubrechen. &lt;br /&gt;
&lt;br /&gt;
;Battery_TargetAchievable_XX: Diese Reading enthält einen Boolean Wert 0|1 und signalisiert ob der im Attribut &#039;&#039;&#039;ctrlBatSocManagementXX-&amp;gt;loadTarget&#039;&#039;&#039; gesetzte Ziel-SoC lt. Prognose am aktuellen Tag tatsächlich erreicht werden kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Nutzung des Batterie SOC-Management ====&lt;br /&gt;
&lt;br /&gt;
Oftmals wird durch die Anlagenhersteller ein hoher statischer SOC eingestellt bzw. empfohlen, der eine längere Verharrungszeit auf einem tiefen SOC-Wert verhindern soll. Allerdings wechseln sich in auch in den Herbst-, Frühlings- und Wintermonaten Zeiten mit wenig PV-Erzeugung mit Phasen stärkerer Solarstrahlung bei klarem Himmel und Sonnenschein ab.&lt;br /&gt;
&lt;br /&gt;
Ein statischer SOC würde dazu führen, dass in den erwähnten Phasen zuviel Energie in das öffentliche Netz eingespeist wird. Dies soll verhindert werden da die wertvolle Energie besser im eigenen Netz gespeichert und in Dunkelphasen verwendet werden kann. Andererseits soll die Batterie nicht zu lange auf einem tiefen SOC verbleiben und außerdem in gewissen Abständen auf einen so hohen SOC geladen werden der den internen Zellausgleich ermöglicht.&lt;br /&gt;
&lt;br /&gt;
Die SOC Kalkulationsroutine des Moduls errechnet unter Berücksichtigung des prognostizierten PV-Ertrages der kommenden Tage, des aktuellen SOC und der im Attribut &#039;&#039;&#039;ctrlBatSocManagementXX&#039;&#039;&#039; (nachfolgend beschrieben) angegebenen Steuerparameter den Vorschlag für eine optimale SOC-Einstellung. Dabei steht &#039;XX&#039; für die Nummer der Batterie, welche mit dem Attribut setupBatteryDevXX korrespondiert.&lt;br /&gt;
&lt;br /&gt;
Durch das Modul selbst findet keine Steuerung der Batterie statt. Es stellt entsprechende Readings zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
::* das Reading &#039;&#039;&#039;Battery_OptimumTargetSoC_XX&#039;&#039;&#039; enthält den vom Modul berechneten optimalen SoC.&lt;br /&gt;
&lt;br /&gt;
::* das Reading &#039;&#039;&#039;Battery_ChargeRequest_XX&#039;&#039;&#039; wird auf &#039;1&#039; gesetzt, wenn der aktuelle SoC unter den optimalen SoC gefallen ist. In diesem Fall sollte die Batterie, unter Umständen mit Netzstrom, zwangsgeladen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Readings können zur Steuerung des SoC (State of Charge) sowie zur Steuerung des verwendeten Ladestroms der Batterie verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Ermittlung des optimalen SOC erfolgt nach folgender Logik:&lt;br /&gt;
&lt;br /&gt;
# 	Ausgehend von &#039;lowSoc&#039; wird der Mindest-SoC kurz vor Sonnenuntergang um 5% inkrementiert sofern am laufenden Tag &#039;maxSoC&#039; nicht erreicht wurde und die PV-Prognose keinen hinreichenden Ertrag des kommenden Tages vorhersagt. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
# 	Wird &#039;maxSoC&#039; (wieder) erreicht, wird Mindest-SoC um 5%, aber nicht tiefer als &#039;lowSoc&#039;, verringert. Ist der berechnete Mindest-SoC größer als &#039;upSoc&#039;, wird der Mindest-SoC auf &#039;upSoc&#039; gesetzt. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
#   Mindest-SoC wird soweit verringert, dass die prognostizierte PV Energie des aktuellen bzw. des folgenden Tages von der Batterie aufgenommen werden kann. Mindest-SoC wird typisch auf &#039;upSoc&#039; und nicht tiefer als &#039;lowSoc&#039; verringert.  &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
# 	Das Modul erfasst den letzten Zeitpunkt am &#039;maxSoC&#039;-Level, um eine Ladung auf &#039;maxSoC&#039; mindestens alle &#039;careCycle&#039; Tage zu realisieren. Zu diesem Zweck wird der optimierte SoC in Abhängigkeit der Resttage bis zum nächsten &#039;careCycle&#039; Zeitpunkt derart verändert, dass durch eine tägliche 5% SoC-Steigerung &#039;maxSoC&#039; am &#039;careCycle&#039; Zeitpunkt rechnerisch erreicht wird. Wird zwischenzeitlich &#039;maxSoC&#039; erreicht, beginnt der &#039;careCycle&#039; Zeitraum erneut. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
# 	Im fünften Schritt werden die Grenzen &#039;lowSoc&#039; und &#039;upSoc&#039; berücksichtigt. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
# 	Im letzten Schritt erfolgt Generierung des Readings &#039;&#039;&#039;Battery_ChargeRequest_XX&#039;&#039;&#039; zur Signalisierung einer eventuell empfohlenen Zwangsladung auf den berechneten Min-SoC. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei sehr wenig Erzeugungungsprognose, d.h. wenn sich der berechnete Min-SoC oberhalb von &#039;upSoC&#039; bewegt, wird &#039;upSoC&#039; eingestellt. Liegt der berechnete Min-SoC unterhalb von &#039;upSoC&#039;, ist jedoch größer als &#039;lowSoC&#039;, wird der berechnete SOC im Reading &#039;&#039;&#039;Battery_OptimumTargetSoC_XX&#039;&#039;&#039; eingestellt. Somit eignet sich die Angabe im Schlüssel &#039;upSoC&#039; um in Zeiten geringer Solarenergieerzeugung die Batterie für eine Notstromerzeugung auf mindestens diesem Wert zu halten. Sollte die PV-Prognose kurzfristig einen größeren Ertrag vorhersagen als die Batterie durch die Einhaltung von &#039;upSoC&#039; aufnehmen kann, wird der Min-SoC unter &#039;upSoC&#039; abgesenkt um möglichst allen PV-Überschuß in der Batterie zu speichern und somit dem Eigenverbrauch zuzuführen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der berechnete Mindest-SoC ist immer nur ein unterer Grenzwert. Die Batterie kann durch Ladevorgänge natürlich jederzeit einen höheren als den im Reading &#039;&#039;&#039;Battery_OptimumTargetSoC_XX&#039;&#039;&#039; dargestellten optimalen SoC haben. Der optimale SoC sollte nicht unterschritten werden um die oben genannten Zusammenhänge abbilden zu können.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Die dynamische SOC Steuerung mit Umsetzungsbeispiel =====&lt;br /&gt;
Das Modul bietet eine Unterstützung zur automatisierten Einstellung eines optimalen SoC Wertes.&lt;br /&gt;
&lt;br /&gt;
Ziel ist es, den Minimum SoC in Abhängigkeit der zu erwartenden Solarerträge des aktuellen und des folgenden Tages so einzustellen, dass der prognostizierte Solarertrag von der Batterie aufgenommen werden kann. Die Einspeisung in das öffentliche Netz soll minimiert werden.&lt;br /&gt;
&lt;br /&gt;
Weiterhin soll ein unterer SoC nicht unterschritten, sowie ein oberer Minimum SoC im Normalfall nicht überschritten werden. Der obere Grenzwert stellt sicher, dass die Batterie mindestens bis zu diesem Wert wieder entladen werden kann. Dadurch bleibt innerhalb der Batterie immer genügend Kapazität verfügbar um unvorhergesehene PV Energie aufnehmen zu können.&lt;br /&gt;
&lt;br /&gt;
Aktiviert wird die Unterstützung der Batteriesteuerung durch das Setzen des Attributes &#039;&#039;&#039;ctrlBatSocManagementXX&#039;&#039;&#039; z.B. mit diesen Angaben:&lt;br /&gt;
&lt;br /&gt;
 ctrlBatSocManagementXX lowSoc=x upSoC=x maxSoC=x careCycle=x &lt;br /&gt;
&lt;br /&gt;
so zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
 ctrlBatSocManagement01 lowSoc=10 upSoC=50 maxSoC=98 careCycle=20&lt;br /&gt;
&lt;br /&gt;
Dabei steht &#039;XX&#039; für die Nummer der Batterie, welche mit dem Attribut setupBatteryDevXX korrespondiert. &lt;br /&gt;
Ist dieses Attribut gesetzt, werden Steuerungs-Readings erstellt, die der Nutzer auswerten und seine Batterieanlage z.B. via notify oder DOIF steuern kann. &amp;lt;bR&amp;gt;&lt;br /&gt;
Das Reading &#039;&#039;&#039;Battery_OptimumTargetSoC_XX&#039;&#039;&#039; enthält den vom Modul berechneten optimalen Mindest-SoC.&lt;br /&gt;
Das Reading &#039;&#039;&#039;Battery_ChargeRequest_XX&#039;&#039;&#039; wird auf &#039;1&#039; gesetzt, wenn der aktuelle SOC unter den optimalen SOC bzw. minimalen SOC gefallen ist.&lt;br /&gt;
&lt;br /&gt;
Die Schlüssel &#039;&#039;&#039;lowSoc&#039;&#039;&#039; und &#039;&#039;&#039;upSoC&#039;&#039;&#039; sind die unteren und oberen Grenzen, zwischen denen sich der Minimum SoC im normalen Betrieb bewegen soll.&lt;br /&gt;
Der Schlüssel &#039;&#039;&#039;maxSoC&#039;&#039;&#039; stellt eine Ladungsgrenze dar, die mindestens alle X (careCycle) Tage erreicht werden soll. Zu diesem Zweck errechnet das Modul die Anzahl der nötigen Erhöhungen der Minimum Batterieladung von 5% pro Tag um ausgehend vom aktuellen SoC-Level. Der resultierende Minimum SoC Wert wird entsprechend angepasst. Wurde der einstellte maxSoC Level erreicht, startet der Wartungszyklus (careCycle) erneut. &amp;lt;br&amp;gt;&lt;br /&gt;
Der maxSoC in Verbindung mit dem durch &#039;&#039;&#039;careCycle&#039;&#039;&#039; definierten Zyklus soll sicherstellen, dass der Batterieverbund in regelmäßigen Abständen einen Ladungsausgleich vollziehen kann. &lt;br /&gt;
&lt;br /&gt;
Durch die Modullogik erfolgt eine 5 protentige Anhebung des Minimum SoC wenn am Vortag maxSoC nicht erreicht wurde. Die Anhebung erfolgt aber nicht über upSoC. upSoC ist die obere Grenze des Minimum SoC. Wird am Vortag mindestens die maxSoC Ladung erreicht, verringert sich der Minimum SoC wieder schrittweise um 5%, aber nicht tiefer als lowSoc.&lt;br /&gt;
&lt;br /&gt;
Ergänzt wird die Logik noch um die PV Vorhersage. Dabei wird die obige Minimum SoC Berechnung um die Erwartung der PV Erwartung am aktuellen und folgenden Tag korrigiert. Das bedeutet, dass der Minimum SoC z.B. von heute 50% auf 10% abgesenkt und dadurch die gespeicherte Energie dem Haushalt zugeführt wird, wenn am heutigen oder kommenden Tag eine entsprechende PV Energie zu erwarten ist um die Batterie wieder vollständig zu zuladen.&lt;br /&gt;
Dadurch ist immer genügend &amp;quot;Freiplatz&amp;quot; in der Batterie und andererseits hat die Batterie in langen Dunkelphasen eine Reserve für einen eventuellen Stromausfall (falls der abgesichert werden soll) und wird vor einem dauerhaften tiefen SoC geschützt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
===== Umsetzungsbeispiel =====&lt;br /&gt;
&lt;br /&gt;
Wie das Reading &#039;&#039;&#039;Battery_OptimumTargetSoC_XX&#039;&#039;&#039; genutzt werden kann, wird am Beispiel einer Victron Batterieanlage gezeigt. Die Anlage wird durch Cerbo GX Gerät gesteuert. Der Cerbo GX ist per MQTT2 in FHEM eingebunden. Umgesetzt ist die Steuerung der Batterie &#039;01&#039;, d.h. die Angabe &#039;XX&#039; ist durch &#039;01&#039; ersetzt.&lt;br /&gt;
&lt;br /&gt;
Die folgende kleine Logik ist in einem &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039; File eingebaut und wird regelmäßig mit&lt;br /&gt;
&lt;br /&gt;
 batSocChargeMgmnt (&#039;&amp;lt;Name SolarForecast Device&amp;gt;&#039;);&lt;br /&gt;
&lt;br /&gt;
aufgerufen. Der Aufruf erfolgt durch Angabe der Routine im Attribut &#039;&#039;&#039;ctrlUserExitFn&#039;&#039;&#039; des SolarForecast Devices:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{  &lt;br /&gt;
  # Hinweis: die Zeichen &#039;::&#039; vor der Routine sind durch das SolarForecast Package bedingt&lt;br /&gt;
  ::batSocChargeMgmnt ($name);&lt;br /&gt;
  return;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Routine wird dadurch automatisch am Ende jedes Zyklus (siehe Attribut plantControl-&amp;gt;cycleInterval) ausgeführt.&lt;br /&gt;
Um die SOC Managementfunktion nach Bedarf ein- bzw. ausschalten zu können wird zunächst im SolarForecast Device ein userattr zur Steuerung der SOC-Logik angelegt:&lt;br /&gt;
&lt;br /&gt;
 attr &amp;lt;Device&amp;gt; userattr userFn_BatterySoCManagement:ein,aus&lt;br /&gt;
&lt;br /&gt;
Die Perl Routine &#039;&#039;&#039;batSocChargeMgmnt&#039;&#039;&#039; ist wie folgt aufgebaut:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
###############################################################################&lt;br /&gt;
#&lt;br /&gt;
# Save this file as 99_mySolarForecastUtils.pm, and create your own functions&lt;br /&gt;
# in the new file. They are then available in every Perl expression.&lt;br /&gt;
#&lt;br /&gt;
###############################################################################&lt;br /&gt;
 package main;&lt;br /&gt;
 use strict;&lt;br /&gt;
 use warnings;&lt;br /&gt;
&lt;br /&gt;
 sub&lt;br /&gt;
 mySolarForecastUtils_Initialize($$)&lt;br /&gt;
 {&lt;br /&gt;
   my ($hash) = @_;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
# ab hier eigenen Code eintragen&lt;br /&gt;
###############################################################################&lt;br /&gt;
&lt;br /&gt;
############################################################################&lt;br /&gt;
#      Batterie SOC und Max. Ladestrom Management&lt;br /&gt;
############################################################################&lt;br /&gt;
sub batSocChargeMgmnt {&lt;br /&gt;
  my $name = shift;&lt;br /&gt;
  my $bn   = &#039;01&#039;;                                                                      # Batterienummer (evtl. im Aufruf mitgeben)&lt;br /&gt;
  my $hash = $defs{$name};&lt;br /&gt;
  &lt;br /&gt;
  my $vebus   = &#039;MQTT2_cerboGX_c0619ab34e08_vebus&#039;;                                     # Victron Vebus Device&lt;br /&gt;
  my $vicsets = &#039;MQTT2_cerboGX_c0619ab34e08_settings&#039;;                                  # Victron Einstellungen&lt;br /&gt;
&lt;br /&gt;
  my $maxcspc = 105;                                                                    # max. Ladestrom (A) für Victron MPII Verbund&lt;br /&gt;
  my $preduce = FHEM::SolarForecast::BatteryVal ($name, $bn, &#039;bpinreduced&#039;, $maxcspc);  # reduzierte Ladeleistung aus Bat-Konfig (W)&lt;br /&gt;
  my $actmcc  = ReadingsNum ($vicsets, &#039;MaxChargeCurrent&#039;, undef);                      # akt. Ladestromeinstellung&lt;br /&gt;
  my $load    = $actmcc // $maxcspc;                                                    # Soll-Ladestrom (A)  &lt;br /&gt;
&lt;br /&gt;
  ## Battery SoC Management&lt;br /&gt;
  ###########################&lt;br /&gt;
  my $ubsm = AttrVal ($name, &#039;userFn_BatterySoCManagement&#039;, &#039;aus&#039;); &lt;br /&gt;
  &lt;br /&gt;
  if ($ubsm eq &#039;ein&#039;) { &lt;br /&gt;
    my $bcrq = ReadingsNum ($name,    &#039;Battery_ChargeRequest_&#039;.$bn,     0);&lt;br /&gt;
    my $csoc = ReadingsNum ($vicsets, &#039;MinimumSocLimit&#039;,               10);             # akt. SoC&lt;br /&gt;
    my $osoc = ReadingsNum ($name,    &#039;Battery_OptimumTargetSoC_&#039;.$bn, 10);             # Soll-SoC&lt;br /&gt;
	my $surp = ReadingsNum ($name,    &#039;Current_Surplus&#039;,                0);             # aktueller PV-Überschuß&lt;br /&gt;
	&lt;br /&gt;
    if ($csoc != $osoc) {&lt;br /&gt;
      CommandSet (undef, &amp;quot;$vicsets MinimumSocLimit $osoc&amp;quot;);&lt;br /&gt;
      Log3 ($name, 3, qq{$name - userFn SoCMgmnt -&amp;gt; MinimumSocLimit in $vicsets set to $osoc %});&lt;br /&gt;
    }&lt;br /&gt;
	                                           &lt;br /&gt;
    if ($bcrq &amp;amp;&amp;amp; $surp &amp;lt; 1000) {                                                       # max. Ladestrom (A) b. Battery_ChargeRequest&lt;br /&gt;
      $load = $preduce / 48;                                                           # bei 48V Batteriesystem&lt;br /&gt;
    }&lt;br /&gt;
	else {&lt;br /&gt;
	  $load = $maxcspc;&lt;br /&gt;
	}&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  ## Einstellung MPII Verbund MaxChargeCurrent&lt;br /&gt;
  ##############################################&lt;br /&gt;
  if ($actmcc &amp;amp;&amp;amp; $load != $actmcc) {&lt;br /&gt;
    CommandSet (undef, &amp;quot;$vicsets MaxChargeCurrent $load&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
    Log3 ($name, 3, qq{$name - userFn ChargeMgmnt -&amp;gt; MaxChargeCurrent in $vicsets set }.&lt;br /&gt;
                    qq{from old $actmcc A to $load A});&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  readingsSingleUpdate ($hash, &#039;userFn_MPII_MaxChargeCurrent_set&#039;, $load, 0);&lt;br /&gt;
  &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
###############################################################################&lt;br /&gt;
# Ende eigener Code&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ist im SolarForecast Device das Reading  &#039;&#039;&#039;Battery_ChargeRequest_01&#039;&#039;&#039; gesetzt, ist der aktuelle SOC kleiner als der optimale SOC und die Batterie wird (eventuell aus dem öffentlichen Netz) geladen. Das kann automatisch durch den Cerbo GX bzw. in anderen Anlagen durch einen entsprechenden Befehl erfolgen.&lt;br /&gt;
Damit die Netzbeladung nicht mit der vollen Ladestromstärke geschieht, wird der Ladestrom auf den gewünschten Wert (hier 21 A -&amp;gt; ca. 1000 W bei einem 48V System) reduziert sofern kein oder zu wenig PV-Überschuß vorhanden ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== weitere mögliche Aspekte der SOC- und Batterieladesteuerung ====&lt;br /&gt;
&lt;br /&gt;
SolarForecast liefert durch seine internen Logiken bereits sehr hilfreiche Signale zur Steuerung von Batteriesystemen in Form von Readings an. Abhängig von den individuellen Möglichkeiten der vorhandenen Anlage käme zum Beispiel zusätzlich zu einer Steurung des optimalen SOC oder der Zeitfenster für die Batterieaufladung auch eine Steuerung des Ladestroms bzw. der verwendeten Ladeleistung in Betracht.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hintergründe:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Das Laden eines Akkus mit möglichst gleichbleibender und geringer Leistung wirkt sich positiv auf dessen Langlebigkeit aus, was u.a. auch daran liegt, dass der Temperaturstreß im Akku bei langsamer Energiezuführung nur gering ist.&lt;br /&gt;
&lt;br /&gt;
* Ein geringer Ladestrom führt zu einem geringen Absacken der Akkuspannung beim (ggf, temporären) Abschalten des Ladestroms, was sich positiv auf die SOC-Schätzung auswirkt.&lt;br /&gt;
    &lt;br /&gt;
* Ein geringer Ladestrom führt, insb. bei Nutzung passiver Balancer (sind in den Akku-Systemen üblicherweise verbaut), zu einer geringen Differenz zwischen den einzelnen Zellspannungen und damit zu einer besseren Kapazitätsausnutzung eins Akkus.&lt;br /&gt;
&lt;br /&gt;
* Ein geringer Ladestrom kann zu einem zeitlich punktgenauen Zielfüllstand (im Winter 100%, ansonsten natürlich weniger) am Ende des Tages führen.&lt;br /&gt;
    &lt;br /&gt;
* Eine geringer Ladestrom führt auch zu geringen Leitungsverlusten, da der Strom quadratisch zu den Leitungsverlusten beiträgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weiterhin kann auch die Verweildauer oder die Begrenzung des SOC, auf den die Batterie maximal geladen werden soll, Gegenstand von Optimierungen sein.&lt;br /&gt;
&lt;br /&gt;
Die häufig zu findende Empfehlung, Akkus nicht zu lange bei einem SOC von 100% verweilen zu lassen, liegt weniger an dem SOC als solchem, sondern daran, dass &#039;&#039;SOC=100%&#039;&#039; üblicherweise dadurch ermittelt wird, dass eine Zelle im Akku einen Spannungsschwellenwert überschreitet, der – würde dieser Schwellenwert zeitlich länger überschritten sein – zu einer Schädigung des Akkus führen könnte. Schädlich sind insbesondere häufige Nachladevorgänge bei Werten um die 100% SOC, da Zellen dann ja häufiger und damit - integral gesehen - auch länger den o.g. Schwellenwert überschreiten.&lt;br /&gt;
&lt;br /&gt;
Einige Akku-Hersteller, z.B. BYD, haben auf o.g, Grund in der (neueren) Firmware Ihrer Systeme einen SOC-Threshold (bei BYD liegt dieser bei 95%) eingebaut. Dieser Wert muss nach einem erreichten SOC von 100% unterschritten  werden, bevor überhaupt eine weitere Ladung erfolgt. Insbesondere in der dunklen Jahreszeit kann es daher sinnvoll sein, den Akku unter Verwendung von PV-Leistung so zu füllen, dass dieser möglichst nur einmal vor Einbruch der Dunkelheit auf &#039;&#039;SOC=100%&#039;&#039; geladen wird. Hierbei sollte darauf geachtet werden, dass der Ladeschlussstrom des jeweiligen Systems nicht unterschritten wird. &lt;br /&gt;
&lt;br /&gt;
SolarForecast unterstützt die oben betrachtete Batteriesteuerung durch die optionale Angabe von &#039;&#039;&#039;loadAbort&#039;&#039;&#039; im Attribut &#039;&#039;&#039;ctrlBatSocManagementXX&#039;&#039;&#039;. Das in diesem Kontext erstellte Reading &#039;&#039;&#039;Battery_ChargeAbort_XX&#039;&#039;&#039; stellt die auswertbaren Steuerungsinformationen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== PV-Prognose und Verbrauch optimierte Beladungssteuerung unter Berücksichtigung einer Wirkleistungsbegrenzung ====&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2025-04-25 143910.png|right|thumb|400px|Batterie Popup &amp;quot;nur laden wenn Einspeiselimit überschritten&amp;quot;]]&lt;br /&gt;
&lt;br /&gt;
Die in diesem Abschnitt beschriebene Ladestrategie &#039;&#039;&#039;loadRelease&#039;&#039;&#039; wird im Modul als &#039;&#039;&#039;Ladefreigabe&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Üblicherweise wird die Batterie geladen. sobald PV-Überschuß vorhanden ist ohne eine Prognose zu berücksichtigen. Dadurch sind die Batterien im Sommer unter Umständen schon um 10-12 Uhr voll geladen und die Anlage liefert ihre mehr oder weniger volle Energie (je nach Ausrichtung) in das öffentliche Netz. Dann könnte es zu einer Begrenzung der PV-Leistung durch einen Schwellenwert (z.B. 70%-Regelung) oder eventuell Zwangsabregelung durch den Netzbetreiber kommen.&lt;br /&gt;
&lt;br /&gt;
Hintergrund dieser Erweiterung ist das Bestreben, eine optimale Unterstützung bei der Maximierung des Eigenverbrauchs zu geben und einen Beitrag zur Netzstabilität zu leisten.&lt;br /&gt;
Das Ziel der Beladungssteuerung der Batterie ist es, die Ladung der Batterie über den Tag verteilt so zu steuern, dass die Batterie erst dann geladen wird, wenn durch den PV-Überschuß eine Netzeinspeisung erfolgen würde bzw. ein Grenzwert erreicht werden würde, der zu einer Abregelung der PV-Anlage führen könnte.&lt;br /&gt;
Es kann auch zu einer Zwangsabregelung durch den Netzbetreiber führen, was vermieden werden soll.&lt;br /&gt;
 &lt;br /&gt;
Die interne Kalkulationslogik erstellt zu diesem Zweck ein Reading &#039;&#039;&#039;Battery_ChargeUnrestricted_XX&#039;&#039;&#039;.&lt;br /&gt;
Dieses Reading gibt dem User ein Signal, ob zu der aktuellen Stunde der Ladevorgang der Batterie freigegeben (1) oder nicht freigegeben werden sollte (0). Die Freigabe zur Ladung bedeutet dass:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Battery_ChargeUnrestricted_XX=1&#039;&#039;&#039; die Batterie mit ihrer vollen, oder der im Reading [[SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#leistungsoptimierte_Beladungssteuerung_mit_dem_Fokus_geringe_Verlustleistung_und_Verschleiß|Battery_ChargeOptTargetPower_XX]] angegebenen Ladeleistung angesteuert werden sollte um wie prognostiziert am Ende des Sonnentages die volle bzw. maximal mögliche Ladung zu erhalten..&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Battery_ChargeUnrestricted_XX=0&#039;&#039;&#039; die Batterie nur geladen werden sollte wenn der im Attribut plantControl-&amp;gt;feedinPowerLimit definierte Wert überschritten wird&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
Ist keine Freigabe zur Ladung vorhanden, kann bzw. sollte dennoch eine Ladung der Batterie vorgenommen werden, sofern eine über dem im &amp;lt;br&amp;gt;&lt;br /&gt;
Attribut &#039;&#039;&#039;plantControl-&amp;gt;feedinPowerLimit&#039;&#039;&#039; definierten Limit liegende Einspeiseleistung vorhanden ist. In diesem Status sollte die Batterie nur mit der Differenzleistung von vorhandener Einspeiseleistung und dem gesetzen Einspeiselimit geladen werden und damit das gesetzte Einspeiselimit einhalten ohne die Anlage abzuregeln. Ist die Batterie in einer Balkengrafikebene dargestellt, wird im Mouse-Over Popup die Battrie mit &#039;&#039;&amp;quot;nur laden wenn Einspeiselimit überschritten&amp;quot;&#039;&#039; gekennzeichnet.&lt;br /&gt;
&lt;br /&gt;
Die Möglichkeiten der Batteriesteuerung sind stark von der individuellen Anlage abhängig und können hier nicht beschrieben werden. Ein Umsetzungsbeispiel mit einer Victron-Anlage mit Pylontech Batterie ist weiter unten dargestellt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
===== Aktivierung und Arbeitsweise =====&lt;br /&gt;
&lt;br /&gt;
Die implementierte Logik zur optimierten Batterie Beladungssteuerung wird aktiviert, wenn folgende Kriterien erfüllt sind: &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* der Schlüssel &#039;&#039;&#039;capacity&#039;&#039;&#039; in den setupInverterDevXX Atributen ist gesetzt&lt;br /&gt;
* das Attribut &#039;&#039;&#039;ctrlBatSocManagementXX&#039;&#039;&#039; für die jeweilige Batterie ist gesetzt&lt;br /&gt;
* der Schlüssel &#039;&#039;&#039;cap&#039;&#039;&#039; im Attribut setupBatteryDevXX der jeweiligen Batterie ist gesetzt&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Die Logik arbeitet wie folgt:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;Der resultierende Schwellenwert der Wirkleistungsbegrenzung: der Gesamtanlage wird durch die Auswertung der in den Inverter-Attributen setupInverterDevXX  gesetzen Schlüssel limit (optional) und capacity ermittelt. Inverter mit gesetztem Schlüssel feed = grid (deren Energie wird ausschließlich in das öffentlich Netz eingespeist) werden von der Betrachtung in diesem Kontext ausgeschlossen.&lt;br /&gt;
&lt;br /&gt;
;Die benötigte Beladung bis zu maxSoC (im Attribut setupBatteryDevXX) : wird ermittelt und mit der zu erwartenden PV Tagesprognose in Beziehung gesetzt. Sofern die PV-Erzeugung abzgl. Verbrauch noch nicht das Wirkleistungbegrenzungs-Limit erreicht UND noch genügend PV-Überschuß für eine Ladung auf &#039;&#039;maxSoC&#039;&#039; im Laufe der kommenden Stunden des Tages zu erwarten ist, bleibt das Reading &#039;&#039;&#039;Battery_ChargeUnrestricted_XX = 0&#039;&#039;&#039;. Die erreichte Qualität der Steuerungslogik ist stark von der Prognosequaliät abhängig. Um die Steuerung resilienter zu gestalten, wird per default ein Sicherheitsaufschlag von 50% auf die prognostizierte benötigte Ladeenenergie auf Stundenbasis einkalkuliert. Dieser Wert kann mit dem Attribut &#039;&#039;&#039;ctrlBatSocManagementXX-&amp;gt;safetyMargin&#039;&#039;&#039; individuell angepasst werden.&lt;br /&gt;
&lt;br /&gt;
;Wird das Wirkleistungbegrenzungs-Limit erreicht/überschritten und/oder die PV-Überschußprognose: zzgl. eines Sicherheitspuffers unterschritten, wird &#039;&#039;&#039;Battery_ChargeUnrestricted_XX = 1&#039;&#039;&#039; gesetzt. Der User kann darauf reagieren und den Batterie Ladevorgang aktivieren. Die Möglichkeiten der Aktivierung sind natürlich von der installierten Anlage abhängig. Das SolarForecast Device greift nicht in die Batteriesteuerung direkt ein.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Anders ausgedrückt erfolgt immer dann eine Ladefreigabe, wenn einer der nachfolgenden Sachverhalte &#039;&#039;wahr&#039;&#039; ist:&lt;br /&gt;
&lt;br /&gt;
- die benötigte Ladeenergie (aller installierten Batterien) &amp;gt;= PV-Restüberschuß des Tages (Reading RestOfDayPVforecast) zzgl. Sicherheitsaufschlag&lt;br /&gt;
&lt;br /&gt;
ODER&lt;br /&gt;
&lt;br /&gt;
- der Zeitpunkt des PV-Erzeugungszenit des laufenden Tages überschritten ist (Reading Today_MaxPVforecastTime)&lt;br /&gt;
&lt;br /&gt;
ODER&lt;br /&gt;
&lt;br /&gt;
- wenn die aktuelle PV Leistung &amp;gt;= einer eventuellen Wechselrichter-Leistungsbegrenzung ist (Summe aller WR-Begrenzungen)&lt;br /&gt;
&lt;br /&gt;
ODER&lt;br /&gt;
&lt;br /&gt;
- wenn das BatSoc-Management _nicht_ aktiviert ist (nicht alle oben genannten Aktivierungskriterien erfüllt sind)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sind mehrere Batterien installiert und durch das Management einer Batterie die Einhaltung der genannten Kriterien bzw. Sachverhalte erwirkt wird, bleiben die weiteren Batterien im Modus Ladefreigabe (Battery_ChargeUnrestricted_XX=1).  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Umsetzungsbeispiel Batterie Ladesteuerung mit Victron GX Venus über den Grid Setpoint =====&lt;br /&gt;
&lt;br /&gt;
Ziel der Steuerung ist es abhängig vom Wert des Readings Battery_ChargeUnrestricted_XX (z.B. Battery_ChargeUnrestricted_01) die Ladung des angeschlossenen Batteriesystems zu aktivieren bzw. zu deaktivieren.&lt;br /&gt;
Das vorliegende Beispiel soll umzusetzen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Battery_ChargeUnrestricted_01 = 0 -&amp;gt; Batterie nicht aufladen bzw. nur laden wenn Einspeiselimit überschritten (Attribut plantControl-&amp;gt;feedinPowerLimit)&lt;br /&gt;
* Battery_ChargeUnrestricted_01 = 1 -&amp;gt; Batterie aufladen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Grid Setpoint ist ein Parameter in den Victron Systemeinstellungen. Das Victron System wird den eingestellten Grid Setpoint versuchen einzuhalten und bei einem positiven Wert Energie aus dem Netz beziehen bzw. bei einem negativen Grid Setpoint Energie in das Netz einspeisen.&lt;br /&gt;
&lt;br /&gt;
Normalerweise wird das Victron System die erzeugte PV Energie zunächst zur Deckung des Hausverbrauchs verwenden, ist dann noch ein PV Überschuß vorhanden, die Batterien aufladen und bei weiter vohandenen PV Überschuß diese Energie in das öffentliche Netz einspeisen sofern dem System eine Einspeisung erlaubt ist. Anderenfalls werden die Wechselrichter oder MPPT Smartloader heruntergeregelt bzw. limitiert. &lt;br /&gt;
&lt;br /&gt;
In der Standardeinstellung ist beim Victron System ein Grid Setpoint von ca. 20 bis 50 Watt, d.h. das System strebt danach diese Vorgabe zu erfüllen zieht ständig Energie in diesem Bereich aus dem öffentlich Netz von einem gelegentlichen Überschwingen der Regelung abgesehen. Damit ein eventueller PV Überschuß nicht zum Laden der Batterie verwendet, sondern statt dessen in das öffentliche Netz eingespeist wird, muß der Grid Setpoint um diesen Betrag abgesenkt werden.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2025-02-24 114403.png|right|thumb|400px|Integration des Grid Setpoint-Managements in den Own_Spec-Bereich]]&lt;br /&gt;
&lt;br /&gt;
Damit man später per Attribut bzw. über den [[#Formatierung_der_Inhalte_im_Bereich_&amp;quot;Graphic_Header_Own_Specification&amp;quot;|Own_Spec-Bereich]] die automatische Ladesteuerung über den Grid Setpoint ein- bzw. ausschalten kann, legen wir ein userattr an:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 userattr &amp;lt;Name&amp;gt; userFn_GridSetpointManagement:ein,aus&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weiterhin sind einige Vorbereitungen im Device MQTT2_cerboGX_c0619ab34e08_settings vorzunehmen. Dieses Device ist im vorliegenden Beispiel ein in FHEM vorhandenes MQTT2_DEVICE, über das die Victron CerboGX Einstellungen zugreifbar sowie einstellbar sind. Die Vorgehensweise zur Erstellung des MQTT2_DEVICE ist hier nicht beschrieben und wird vorausgesetzt.&lt;br /&gt;
&lt;br /&gt;
Damit ein entsprechender Set-Befehl zur Einstellung des Grid Setpoints vorhanden ist, werden folgende Attribute gesetzt:&lt;br /&gt;
&lt;br /&gt;
 attr MQTT2_cerboGX_c0619ab34e08_settings jsonMap Settings_CGwacs_AcPowerSetPoint_value:GridSetpoint&lt;br /&gt;
 attr MQTT2_cerboGX_c0619ab34e08_settings setList GridSetpoint W/c0619ab34e08/settings/0/Settings/CGwacs/AcPowerSetPoint {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
&lt;br /&gt;
Nach dieser Attributierung kann mit einem &amp;quot;&#039;&#039;set MQTT2_cerboGX_c0619ab34e08_settings GridSetpoint &amp;lt;Wert&amp;gt;&#039;&#039;&amp;quot; der Grid Setpoint eingestellt werden.&lt;br /&gt;
&lt;br /&gt;
Das Attribut userFn_GridSetpointManagement kann nun im Perl Code ausgewertet werden um das Grid Setpoint-Management ein- bzw. auszuschalten.&lt;br /&gt;
Der nachfolgende Code wird in die Datei &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039; eingefügt. Ist diese Datei nicht vorhanden, legen wir sie vorab als Kopie der ausgelieferten 99_myUtils.pm an.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Code Management Batterieladung über Victron Grid Setpoint -&amp;gt;&#039;&#039;&#039; &amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
###############################################################################&lt;br /&gt;
#&lt;br /&gt;
# Save this file as 99_mySolarForecastUtils.pm, and create your own functions&lt;br /&gt;
# in the new file. They are then available in every Perl expression.&lt;br /&gt;
#&lt;br /&gt;
###############################################################################&lt;br /&gt;
 package main;&lt;br /&gt;
 use strict;&lt;br /&gt;
 use warnings;&lt;br /&gt;
&lt;br /&gt;
 sub&lt;br /&gt;
 mySolarForecastUtils_Initialize($$)&lt;br /&gt;
 {&lt;br /&gt;
   my ($hash) = @_;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
# ab hier eigenen Code eintragen&lt;br /&gt;
###############################################################################&lt;br /&gt;
&lt;br /&gt;
############################################################################&lt;br /&gt;
#   Steuerung Start/Stop der Batterieladung über den Victron Grid Setpoint&lt;br /&gt;
############################################################################&lt;br /&gt;
sub gridSetpointMgmt {&lt;br /&gt;
  my $name = shift;&lt;br /&gt;
  my $hash = $defs{$name};&lt;br /&gt;
  &lt;br /&gt;
  my $vicsets   = &#039;MQTT2_cerboGX_c0619ab34e08_settings&#039;;                               # Device Victron Einstellungen&lt;br /&gt;
  my $gspdef    = 20;                                                                  # Default Einstellung des Grid Setpoint&lt;br /&gt;
  my $targetgsp = $gspdef;                                                             # Voreinstellung Grid Setpoint&lt;br /&gt;
  my $neggspmax = FHEM::SolarForecast::CurrentVal ($name, &#039;feedinPowerLimit&#039;, 5000);   # max. gewünschte Einspeiseleistung in Watt&lt;br /&gt;
  $neggspmax    = $neggspmax * -1;&lt;br /&gt;
  my $gspm      = AttrVal ($name, &#039;userFn_GridSetpointManagement&#039;, &#039;aus&#039;);             # Freischaltung des Grid Setpoint-Managements&lt;br /&gt;
  my $curgsp    = ReadingsNum ($vicsets, &#039;GridSetpoint&#039;,         $gspdef);             # aktuelle Einstellung Grid Setpoint&lt;br /&gt;
  &lt;br /&gt;
  if ($gspm eq &#039;ein&#039;) { &lt;br /&gt;
    my $bcrq = ReadingsNum ($name, &#039;Battery_ChargeUnrestricted_01&#039;, 0);         # Ladefreigabe (1 -&amp;gt; Bat Ladefreigabe)&lt;br /&gt;
    my $surp = ReadingsNum ($name, &#039;Current_Surplus&#039;,               0);         # aktueller PV-Überschuß&lt;br /&gt;
	&lt;br /&gt;
    if (!$bcrq) {                                                               # Grid Setpoint absenken wenn keine .. &lt;br /&gt;
        $targetgsp = $gspdef - $surp;                                           # ..Batterie Ladefreigabe&lt;br /&gt;
        $targetgsp = $neggspmax if($targetgsp &amp;lt; $neggspmax);                    # Begrenzung der Einspeiseleistung&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if ($curgsp != $targetgsp) {&lt;br /&gt;
	  fhem (&amp;quot;set $vicsets GridSetpoint $targetgsp&amp;quot;);&lt;br /&gt;
      Log3 ($name, 3, qq{$name - userFn gridSetpointMgmt -&amp;gt; GridSetpoint in $vicsets set to $targetgsp});&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  readingsSingleUpdate ($hash, &#039;userFn_Victron_GridSetpoint_set&#039;, $targetgsp, 0);&lt;br /&gt;
  &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
###############################################################################&lt;br /&gt;
# Ende eigener Code&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Vergleich des Setpoints mit der Vorgabe von &#039;&#039;$neggspmax&#039;&#039; dient dazu die maximale Einspeiseleistung in das öffentliche Netz zu begrenzen. Ist diese Grenze erreicht, wird der noch verbleibende Überschuß in die Batterie geladen. Dadurch wird eine Abregelung der Anlage vermieden wenn der Schwellenwert &#039;&#039;$neggspmax&#039;&#039;erreicht ist sofern die Batterie aufnahmefähig ist und nicht voll geladen ist. &lt;br /&gt;
&lt;br /&gt;
Damit die Routine mit jedem Zyklus im Modul ausgeführt wird, legen wir sie im Attribut &#039;&#039;&#039;ctrlUserExitFn&#039;&#039;&#039; an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;Name&amp;gt; ctrlUserExitFn  {  &lt;br /&gt;
                               ::gridSetpointMgmt  ($name);&lt;br /&gt;
                            }&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zum Management des automatischen Grid Setpoint und der Anzeige des eingestellten Wertes wird das Attribut und die Readinganzeige in den Own_Spec-Bereich eingebunden, z.B.:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 attr &amp;lt;Name&amp;gt; graphicHeaderOwnspec  #Settings&lt;br /&gt;
                                   SetPoint&amp;amp;nbsp;Management:userFn_GridSetpointManagement&lt;br /&gt;
                                   Grid&amp;amp;nbsp;Setpoint:GridSetpoint@MQTT2_cerboGX_c0619ab34e08_settings&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== leistungsoptimierte Beladungssteuerung mit dem Fokus geringe Verlustleistung und Verschleiß ====&lt;br /&gt;
&lt;br /&gt;
Über das Attribut ctrlBatSocManagementXX kann mit dem Schlüssel loadStrategy die Ladestrategie &#039;&#039;&#039;optPower&#039;&#039;&#039; oder &#039;&#039;&#039;smartPower&#039;&#039;&#039; eingestellt werden.&lt;br /&gt;
Das SF-Modul bietet ein weiteres Reading &#039;&#039;&#039;Battery_ChargeOptTargetPower_XX&#039;&#039;&#039; zur Batteriesteuerung an. Dieses Reading liefert eine kalkulierte optimale Ladeleistung in Watt.&lt;br /&gt;
&lt;br /&gt;
Die in diesem Abschnitt beschriebene Ladestrategie &#039;&#039;&#039;optPower&#039;&#039;&#039; wird im Modul als &#039;&#039;&#039;optimierte Ladeleistung&#039;&#039;&#039;, die Ladestrategie &#039;&#039;&#039;smartPower&#039;&#039;&#039; als &#039;&#039;&#039;zieloptimierte Ladeleistung&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beide Strategien kalkulieren die Ladeleistung kontinuierlich neu und berücksichtigen dabei:&lt;br /&gt;
&lt;br /&gt;
* die Prognose der PV-Erzeugung der nächsten Stunden und des (Rest-)Tages&lt;br /&gt;
* den prognostizierten Verbrauch auf Stunden- bzw. Tagesbasis&lt;br /&gt;
* eine eventuell mögliche Überschreitung des gesetzten Einspeiselimits (plantControl-&amp;gt;feedinPowerLimit)&lt;br /&gt;
* die gesetzten Leistungsparameter der Batterie &lt;br /&gt;
* das Ziel, am Tagesende einen möglichst maximalen SoC der Batterie erreicht zu haben bzw. den eingestellten Ziel-SoC &#039;&#039;&#039;loadTarget&#039;&#039;&#039;&lt;br /&gt;
* die Effizienz der Batterieanlage (Einstellung &#039;&#039;&#039;setupBatteryDevXX-&amp;gt;efficiency&#039;&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Fokus der implementierten Logik liegt dabei auf einer kontinuierlichen Ladeleistung, die aber unter den oben genannten Gesichtspunkten möglichst (relativ) gering sein soll um den Batterieverschleiß und die Verlustleistung zu minimieren. Die im Reading angezeigte Ladeleistung kann mit geeigneten Mitteln direkt in die Leistungssteuerung der Batterie eingebracht werden. Natürlich kann die Leistung auch vorab in einen optimalen Ladestrom umgerechnet werden.&lt;br /&gt;
&lt;br /&gt;
Da das Ergebnis der Kalkulation stark von den Prognosen abhängt, ist zur Erhöhung der Resilienz per Default ein Sicherheitszuschlag von 20% eingebaut. Dieser Wert kann mit dem Attribut &#039;&#039;&#039;ctrlBatSocManagementXX-&amp;gt;safetyMargin&#039;&#039;&#039; individuell angepasst werden. &lt;br /&gt;
&lt;br /&gt;
Ist kein PV-Überschuß (mehr) vorhanden / prognostiziert oder das Ladeziel erreicht, erfolgt ein Rückfall der Ladeleistung im Reading Battery_ChargeOptTargetPower_XX auf den  im Attribut &#039;&#039;&#039;setupBatteryDevXX-&amp;gt;pinmax&#039;&#039;&#039; angegebenen Wert. Ist dieser Wert nicht gesetzt, erfolgt der Rückfall auf &amp;quot;Unendlich&amp;quot; (9223372036854775807). &lt;br /&gt;
 &lt;br /&gt;
Im Reading Battery_ChargeOptTargetPower_XX wird der Wert des Parameters &#039;&#039;&#039;setupBatteryDevXX-&amp;gt;pinreduced&#039;&#039;&#039; gesetzt, wenn weniger Ladeleistung als pinreduced berechnet wurde (Einhaltung einer Mindestladeleistung) oder wenn der SoC der Batterie &amp;lt;= [[#Aktivierung_des_Batterie_SOC-_und_Lade-Managements|lowSoC]] beträgt.&lt;br /&gt;
Somit wird die Batterie bei einer Anforderungsladung bei Unterschreitung von lowSoC mit nur wenig Leistung aus dem Grid geladen falls es nötig sein sollte.&lt;br /&gt;
Es ist demzufolge ratsam den optionalen Parameter &#039;&#039;&#039;setupBatteryDevXX-&amp;gt;pinreduced&#039;&#039;&#039; zu setzen, da ansonsten ebenfalls ein Rückfall auf &#039;&#039;&#039;setupBatteryDevXX-&amp;gt;pinmax&#039;&#039;&#039; bzw. &amp;quot;Unendlich&amp;quot; erfolgt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beide Strategien, optPower und smartPower, setzen die zuvor beschriebene Arbeitsweise um.&lt;br /&gt;
Ergänzend wird bei der Ladestrategie &#039;&#039;&#039;smartPower&#039;&#039;&#039; die generelle Erreichbarkeit des Ladeziels bei der Festlegung der Ladeleistung berücksichtigt. Es führt dazu dass:&lt;br /&gt;
&lt;br /&gt;
* die Ladeleistung im Reading Battery_ChargeOptTargetPower_XX auf einen tendenziell höheren Wert als bei der optPower-Strategie gesetzt wird, wenn/solange das eingestellte Ladeziel als unerreichbar kalkuliert wird&lt;br /&gt;
* bei kalkulierter Erreichbarkeit des Ladeziels der Sicherheitsaufschlag linear absenkend proportional zum Kehrwert des verfügbaren PV-Überschusses auf die Ladeleistung angewendet wird&lt;br /&gt;
&lt;br /&gt;
Der nächste Abschnitt erläutert die smartPower Funktionen etwas eingehender.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
===== smartPower - die zieloptimierte Ladeleistung  =====&lt;br /&gt;
Die hinter smartPower liegende Anpassungslogik wird nachfolgend etwas näher beschrieben.&lt;br /&gt;
Ausgangspunkt ist das Ergbnis einer Ratio-Funktion aus dem (Rest)Tages-PV-Überschuß und benötigter Energie zur Erlangung des Batterie Ladeziels.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Lineare Interpolation zwischen pinmax und abgesicherter minpower&#039;&#039;&#039;  &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Variablen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;P&#039;&#039;&#039;: resultierende Leistung.&lt;br /&gt;
* &#039;&#039;&#039;pinmax&#039;&#039;&#039;: maximale Ladeleistung.&lt;br /&gt;
* &#039;&#039;&#039;limpower&#039;&#039;&#039;: Leistungslimit, d.h. Ladeleistungssollwert.&lt;br /&gt;
* &#039;&#039;&#039;r&#039;&#039;&#039;: Verhältnis in Prozent (r = spday·100 / whneed).&lt;br /&gt;
* &#039;&#039;&#039;otpMargin&#039;&#039;&#039;: Prozentwert der Bandbreite, über die die Absenkung linear verläuft (die Steilheit kann mit dem Attributwert ctrlBatSocManagementXX-&amp;gt;safetyMargin im Device eingestellt werden).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Kennlinie der für smartPower eingesetzten Funktion ist stückweise definiert über das Verhältnis Ratio &#039;&#039;𝑟 = spday x 100 / &amp;lt;benötigte Ladeenergie bis Ziel&amp;gt;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
* Für 𝑟 ≤ 100: &#039;&#039;𝑃 = pinmax&#039;&#039;, Interpretation: Es ist viel Bedarf vorhanden bzw. Überschuss ≤ 100 % des Bedarfs → maximal laden.&lt;br /&gt;
* Für 𝑟 ≥ 100 + otpMargin: &#039;&#039;𝑃 = limpower ⋅ (1 + otpMargin / 100 )&#039;&#039;&lt;br /&gt;
* Für 100 &amp;lt; 𝑟 &amp;lt; 100 + otpMargin: lineare Absenkung von &#039;&#039;&#039;P&#039;&#039;&#039; mit zunehmendem &#039;&#039;&#039;r&#039;&#039;&#039; für den Bereich 100 &amp;lt; 𝑟 &amp;lt; 100 + otpMargin. &#039;&#039;&#039;P&#039;&#039;&#039; wird bei 𝑟 = 100 gleich &#039;&#039;&#039;pinmax&#039;&#039;&#039; und bei 𝑟 = 100 + otpMargin gleich &#039;&#039;limpower ⋅ ( 1 + otpMargin/100 )&#039;&#039;, Interpretation: Sobald der Überschuss über den Bedarf hinausgeht (ratio &amp;gt; 100), wird das Ladeleistungslimit schrittweise von pinmax Richtung limpower reduziert; die Reduktion erfolgt gleichmäßig über die Margin-Spanne.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Lineare Interpolation und Steigung&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Differenz &#039;&#039;(pinmax − limpower)&#039;&#039; wird gleichmäßig auf die otpMargin-Prozentpunkte verteilt. Die Steigung der Geraden ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
slope = − (pinmax − limpower) / otpMargin&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
das heißt die Leistung sinkt um diesen Betrag pro Prozentpunkt von &#039;&#039;&#039;r&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Nach Umformung ist der Zusammenhang wie folgt darstellbar:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
𝑃 = pinmax − ( pinmax − limpower) ⋅ ((𝑟 − 100) / otpMargin)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und ist äquivalent zu&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
𝑃 = pinmax + ( 𝑟 − 100 ) ⋅ slope&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Numerisches Beispiel&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;limpower&#039;&#039;&#039; = 1000 W, &#039;&#039;&#039;pinmax&#039;&#039;&#039; = 3000 W, &#039;&#039;&#039;otpMargin&#039;&#039;&#039; = 20&lt;br /&gt;
* Unterschied = 2000 W, Steigung = −2000/20 = −100 W/%&lt;br /&gt;
* Für &#039;&#039;&#039;r = 105&#039;&#039;&#039;: 𝑃 = 3000 − 2000 ⋅ ( 5 / 20 ) = 3000 − 500 = 2500 W&lt;br /&gt;
* Für &#039;&#039;&#039;r = 120&#039;&#039;&#039; ergibt die Formel den abgesicherten Endwert 1200 W&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Formel sorgt dafür, dass die Zielleistung stufenlos und vorhersehbar reduziert wird, wenn der relative Rest-Überschuss des Tages über 100% steigt, und erreicht bei Ende der otpMargin den geplanten abgesicherten Wert.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Ratio Kennlinie.png|right|thumb|200px|abfallend proportionale Anpassungsfunktion]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In einem grafischen Diagramm stellt sich die abfallend proportionale Anpassungsfunktion der resultierenden Soll-Ladeleistung wie nebenstehend abgebildet dar. Dabei entspricht pow=100 dem jeweiligen pinmax Wert (hier 3000 W). So ist auch pow=0 mit dem abgesicherten Endwert (hier 1200 W) gleichzusetzen. Die Linie verläuft waagerecht bei pinmax solange das das Ratio  r ≤ 100 % und knickt an r == 100 mit steigenden r nach unten. Es verringert sich pow linear von pinmax zu limpower zzgl. otpMargin.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Durch diese Maßnahmen wird eine flexible Lademöglichkeit bis pinmax erreicht, die sich bei unvorhergesehenen Aufklarungen mit starker Sonneneintrahlung bei ansonsten sehr bedeckten Himmel positiv auswirkt. Andererseits wird die Erhöhung der Ladeleistung weiter begrenzt, wenn viel PV-Überschuß in Verbindung mit einer generellen Erreichbarkeit des Ladeziels zum Ende des Tages prognostiziert ist. Man könnte die Ladestrategie &#039;&#039;&#039;smartPower&#039;&#039;&#039; in ihrer Wirkung als einen Mix aus &#039;&#039;&#039;optPower&#039;&#039;&#039; und &#039;&#039;&#039;loadRelease&#039;&#039;&#039; ansehen.&lt;br /&gt;
&lt;br /&gt;
Zusammenfassend ist optPower vorwiegend für die Nutzergruppe geeignet, die eine tendenziell sparsamere Ladeleistung bevorzugen und dafür bereit sind eventuelle &amp;quot;Verluste&amp;quot; in Form von Einspeisung zu akzeptieren. &amp;lt;br&amp;gt;&lt;br /&gt;
Demgegenüber eignet sich das aggressivere smartPower für Anwender, die bei niedrigen PV-Erträgen bewusst das Maximum an Batterieladung anstreben und dafür in Kauf nehmen, dass Ladeleistungen weniger knapp kalkuliert sind. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Unterstützung des Modus &amp;quot;Eigennutzung&amp;quot; bei Hybrid-Wechselrichtern =====&lt;br /&gt;
Viele Hybrid-Wechselrichter verteilen die zur Verfügung stehende PV-Leistung nach folgendem Prioritäten, wenn der (übliche) Modus &amp;quot;Eigennutzung&amp;quot; aktiviert ist:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# Nutzung der Leistung zur Deckung des Hausverbrauchs,&lt;br /&gt;
# Nutzung der Leistung zur Ladung des bzw. der angeschlossenen Speicher.&lt;br /&gt;
# Einspeisung der Leistung in das öffentliche Netz.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Da in aller Regel die Leistung, mit der ein angeschlossener Speicher geladen wird, limitiert werden kann, lassen sich die o.g. Priorität bzgl. des Speichers zugunsten einer Überschusseinspeisung aufweichen. Ein entstehender &amp;quot;Überschuss&amp;quot; steht also ggf. nur deshalb zur Verfügung, weil die Ladeleistung des Speichers begrenzt wurde.&lt;br /&gt;
&lt;br /&gt;
Ab Version 1.58.6 bietet SolarForecat die Möglichkeit, prognostizierte Verbräuche nicht mehr komplett bei der Bestimmung der &#039;&#039;optimalen Ladeleistung&#039;&#039; zu berücksichtigen, sondern nur noch zu einem prozentualen Anteil von 0..100 %. Hierzu muss der Attribut-Schlüssel &#039;&#039;&#039;ctrlBatSocManagementXX-&amp;gt;weightOwnUse=&amp;lt;Percentage&amp;gt;&#039;&#039;&#039; entsprechend gesetzt werden. &lt;br /&gt;
&lt;br /&gt;
So führt beispielsweise weightOwnUse=100 dazu, dass bei der Berechnung der optimalen Ladeleistung prognostizierte Verbräuche nicht mehr berücksichtigt werden indem sie bei der Bestimmung des stündlichen PV-Überschusses unberücksichtigt bleiben. Eine derartige Einstellung kann z.B. sinnvoll sein, wenn alle ansonsten täglich laufende Verbraucher zugunsten einer Batterieladung ausgeschaltet werden können.&lt;br /&gt;
&lt;br /&gt;
Ein Wert von zum Beispiel weightOwnUse=30 lässt 30% der stündlichen Verbrauchslast unberücksichtigt, sodass sich der kalkulierte PV-Überschuß pro Stunde um 30% des prognostizierten Verbrauchs erhöht. Da die Berechnung der optimierten Ladeleistung mit dem zur Verfügung stehenden PV-Überschuß gewichtet erfolgt, würde sich die kalkulierte Ladeleistung in den meisten Fällen ebenfalls um einen gewissen Betrag erhöhen sofern andere Faktoren dies nicht verhindern. Das kann beispielweise der Fall sein, wenn die kalkulierte Ladeleistung zu gering ist und automatisch aif mindestens &#039;&#039;&#039;setupBatteryDevXX-&amp;gt;pinreduced&#039;&#039;&#039; angehoben wurde.&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Welche Ladestrategie soll ich wählen? - eine Möglichkeit zur Best-Practice Findung mit Codebeispiel ====&lt;br /&gt;
&lt;br /&gt;
In den vorangegangenen Abschnitten wurden die verschiedenen Möglichkeiten zur Steuerung der Batterieladung und dem SoC vorgestellt. Diese Steuerungsvarianten optimieren die Arbeitweise der Batterie, haben aber jeweils einen eigenen Fokus auch wenn es gewisse Überschneidungen gibt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Steuerungsvarianten lassen sich folgendermaßen klassifizieren:&lt;br /&gt;
&lt;br /&gt;
# Steuerung der Batterieladung über eine Ladefreigabe, die durch bestimmte Indikatoren festgelegt wird. Diese Variante ist in [[SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#PV-Prognose_und_Verbrauch_optimierte_Beladungssteuerung_unter_Berücksichtigung_einer_Wirkleistungsbegrenzung|diesem]] Abschnitt beschrieben.&lt;br /&gt;
# Steuerung der Batterieladung durch eine optimierte maximale Ladeleistung. Diese Variante ist in [[SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#leistungsoptimierte_Beladungssteuerung_mit_dem_Fokus_geringe_Verlustleistung_und_Verschleiß|diesem]] Abschnitt dargelegt. Zu dieser Variante gehören die Strategien &#039;&#039;optPower&#039;&#039; sowie &#039;&#039;smartPower&#039;&#039;. Die Unterschiede beider Strategien sind ebenfalls in dem verlinkten Abschnitt beschrieben.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Variante 1.)&#039;&#039;&#039; gibt die Ladung der Batterie frei wenn der (verbleibende) PV-Überschuß des Tages kleiner oder gleich dem Ladungsbedarf der Batterie ist um einen SoC von 100% bzw. den maximal möglichen SoC am Ende des Tages zu erreichen. Dadurch wird die in der Batterie vorhandene freie Kapazität so lange wie möglich der Einhaltung von besonderen Grenzwerten, z.B. des Einspeiselimits (plantControl-&amp;gt;feedinPowerLimit), vorbehalten. Nebeneffekt ist, dass plötzlich und unerwartet auftretende hohe Erzeugungsleistungen in die Batterie geladen werden können und nicht unerwünscht in das öffentliche Netz eingespeist werden. &lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Variante 2.)&#039;&#039;&#039; lässt die Batterie kontinuierlich laden, wobei die Ladeleistung auf ein optimiertes Minimum reduziert wird ohne das Ziel, einen maximalen SoC am Tagesende zu erreichen, zu ignorieren. Dadurch wird die Batterie schonend, mit möglichst wenig Verlustleistung und verschleißarm geladen. Die Vorhaltung der freien Kapaziäten der Batterie sind in dieser Variante nicht die Prämisse. Die Einhaltung des gesetzten Enispeiselimits wird auch in dieser Variante beachtet sofern es der Ladezustand der Batterie zulässt. Technisch bedingt kann diese Variante nur ungenügend auf unerwartete PV-Überschüsse reagieren, was zu einer Einspeisung in das öffentliche Netz oder im ungünstigsten Fall zur Abregelung der Anlage führen kann. Mit dem Attribut &#039;&#039;&#039;ctrlBatSocManagementXX-&amp;gt;safetyMargin&#039;&#039;&#039; kann man diesem negativen Aspekt in gewissem Maße entgegenwirken.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ausgehend von diesen unterschiedlichen Optimierungszielen bietet sich die Variante 1.) vorwiegend in den Übergangs- und Wintermonaten an. Durch die beschriebenen Eigenschaften dieser Variante ist bei tendeziell weniger PV-Überschuß über den Tag die Ladung der Batterie komplett freigegeben, wodurch bei plötzlich auftretenden sonnige Abschnitten und Aufklarungen die Batterie sofort mit dem verfügbaren PV-Überschuß geladen wird. Auch eine Einspeisung über das Einspeiselimit hinaus kann verhindert werden. In der übrigen Zeit wird die Ladeleistung durch den verfügbaren PV-Überschuß begrenzt. &lt;br /&gt;
Weiterhin soll der SoC nicht dauerhaft tief absinken und ebenfalls nicht auf einem zu hohen Level verharren was auch einer maximal möglichen Energiespeicherung an sonnigen Tagen entgegenwirken würde.&lt;br /&gt;
In dieser Zeit ist die Nutzung der Kombination von [[SolarForecast_-_Solare_Prognose_(PV_Erzeugung)_und_Verbrauchersteuerung#Nutzung_des_Batterie_SOC-Management|SoC-Management]] und Variante 1.) wohl am Besten geeignet um die Ziele zu verwirklichen sowie Notreserven in den Batterien vorzuhalten. &lt;br /&gt;
&lt;br /&gt;
In den Sommermonaten hingegen ist üblicherweise sehr viel PV-Energie und Überschuß vorhanden. In dieser Periode würden die Batterien mit sehr hoher Leistung sehr zeitig am Tag vollgeladen. Die Variante 1.) ist im Prinzip ebenfalls gut geeignet den Zeitpunkt der Vollladung hinauszuzögern, jedoch erscheint die Varinte 2.) in dieser Zeit besser geeignet mit einer über den gesamten Tag verteilten moderaten Ladeleistung den Ziel-SoC zu erreichen. Auch diese Variante versucht eine Überschreitung des Einspeiselimits zu verhindern sofern der Ladezustand der Batterie dies ermöglicht.&lt;br /&gt;
&lt;br /&gt;
Das nachfolgende Codebeispiel bietet die Möglichkeit über ein Attribut die Ladevariante auszuwählen, über ein weiteres Attribut die SoC-Steuerung zu aktivieren/deaktivieren und integriert auch die Ladesteuerung für eine Batterie-Anforderungsladung bei Unterschreitung von ctrlBatSocManagementXX-&amp;gt;lowSoc. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die gesteuerte Batterieanlage besteht aus:&lt;br /&gt;
&lt;br /&gt;
* 3 x Victron MultiPlusII 3000/48&lt;br /&gt;
* Steuergerät CerboGX (in FHEM über MQTT eingebunden)&lt;br /&gt;
* einem Batteriestack bestehend aus Pylontech US3000C (wird durch CerboxGX gesteuert)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zur Umschaltung bzw. Auswahl der Ladestrategie und des SoC-Managements werden zwei User-Attribute im SolarForecast Device angelegt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr ... userattr userFn_BatterySoCManagement:Ein,Aus userFn_BatteryLoadManagement:Aus,loadRelease,optPower,smartPower&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Im Code werden diese Attribute ausgewertet. Das nachfolgende Script vereint folgende Funktionalitäten:&lt;br /&gt;
&lt;br /&gt;
* die Aktivierung/Deaktivierung des SoC-Managements via Attribut userFn_BatterySoCManagement&lt;br /&gt;
* die Auswahl der Ladestrategie oder Deaktivierung der Ladesteuerung via attribut userFn_BatteryLoadManagement&lt;br /&gt;
* Integration einer Anforderungsladung bei Signalisierung durch Reading Battery_ChargeRequest_XX&lt;br /&gt;
* Generierung von Readings userFn_Victron_GridSetpoint_set und userFn_MPII_MaxChargeCurrent_set zur Dokumentation der eingestellten Soll-Werte&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
Bei der Nachnutzung des Scripts sind zunächst die &#039;&#039;&#039;Konstanten&#039;&#039;&#039; und das Device &#039;&#039;&#039;MQTT2_cerboGX_c0619ab34e08_setting&#039;&#039;&#039;s sowie dessen Readings im Block &#039;&#039;&#039;aktuelle Indikatoren&#039;&#039;&#039; anzupassen.&lt;br /&gt;
&lt;br /&gt;
Das Script wird in die Datei &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039; eingetragen und im SolarForecast Attribut &#039;&#039;&#039;ctrlUserExitFn&#039;&#039;&#039; hinterlegt: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr ... ctrlUserExitFn {&lt;br /&gt;
                          ::batLoadMgmnt ($name, &#039;01&#039;);&lt;br /&gt;
                          # ::batLoadMgmnt ($name, &#039;02&#039;);   # optional für eine weitere Batterie &#039;02&#039;&lt;br /&gt;
                          # ::batLoadMgmnt ($name, &#039;0X&#039;);   # optional für weitere Batterien&lt;br /&gt;
                        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Mit diesem Script ist der Nutzer in der Lage, das Batterie Lademanagement entsprechend seiner Motivation und gegebenenfalls abhängig von der Jahreszeit umzustellen und das Ergebnis bzw. die Zielerreichung zu testen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Attribut &#039;&#039;&#039;userFn_BatteryLoadManagement&#039;&#039;&#039; selektiert die gewünschte Ladestrategie:&lt;br /&gt;
&lt;br /&gt;
;Aus: das Lademangement ist ausgeschaltet &lt;br /&gt;
&lt;br /&gt;
;loadRelease: das Lademangement Variante 1.) Ladestrategie Ladefreigabe ist aktiviert &lt;br /&gt;
&lt;br /&gt;
;optPower: das Lademangement Variante 2.) Ladestrategie optimierte Ladeleistung ist aktiviert &lt;br /&gt;
&lt;br /&gt;
;smartPower: das Lademangement Variante 2.) Ladestrategie zieloptimierte Ladeleistung ist aktiviert&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die selektierte Ladestrategie wird im Script in das SolarForecast Device repliziert, d.h. der Attributschlüssel ctrlBatSocManagement01-&amp;gt;loadStrategy wird identisch zum Wert des Attributes userFn_BatteryLoadManagement gesetzt. Dazu wird das Set-Kommando &#039;&#039;attrKeyVal&#039;&#039; verwendet. Das globale Attribut &#039;&#039;autosave&#039;&#039; sollte aus diesem Grund NICHT explizit auf &amp;quot;0&amp;quot; gesetzt sein um eine reibungslose Funktion zu ermöglichen. &lt;br /&gt;
&lt;br /&gt;
Hat man die individuell passende Management-Strategie gefunden, bietet sich auch eine automatische (zum Beispiel durch einen Kalender oder Sonnenstand (Elevation) zur Mittagszeit) Umschaltung der Ladestrategie an. Die Unterschreitung der Elevation unter einen bestimmten Wert würde den Beginn des Winterhalbjahres und reverse Überschreitung den Beginn des Sommerhalbjahres kennzeichnen. Entsprechend würde dann die gewünschte Ladestrategie selektiert. Alternativ könnte auch die PV-Überschußprognose für den aktuellen Tag über das Reading &#039;&#039;Today_PVforecast&#039;&#039; für eine Entscheidung bzgl. der Ladestrategie herangezogen werden.&lt;br /&gt;
&lt;br /&gt;
Das SoC-Management kann über das gesamte Jahr aktiviert bleiben, hat jedoch nach bisherigen Erfahrungen nur im Winterhalbjahr eine starke Relevanz.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Code Lademanagement -&amp;gt;&#039;&#039;&#039; &amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
###############################################################################&lt;br /&gt;
#&lt;br /&gt;
# Save this file as 99_mySolarForecastUtils.pm, and create your own functions&lt;br /&gt;
# in the new file. They are then available in every Perl expression.&lt;br /&gt;
#&lt;br /&gt;
###############################################################################&lt;br /&gt;
 package main;&lt;br /&gt;
 use strict;&lt;br /&gt;
 use warnings;&lt;br /&gt;
&lt;br /&gt;
 sub&lt;br /&gt;
 mySolarForecastUtils_Initialize($$)&lt;br /&gt;
 {&lt;br /&gt;
   my ($hash) = @_;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
# ab hier eigenen Code eintragen&lt;br /&gt;
###############################################################################&lt;br /&gt;
&lt;br /&gt;
############################################################################&lt;br /&gt;
#      Komplettes Batterie Lademanagement&lt;br /&gt;
############################################################################&lt;br /&gt;
sub batLoadMgmnt {&lt;br /&gt;
  my $name = shift;&lt;br /&gt;
  my $bn   = shift // &#039;01&#039;;                                                             # Batterienummer&lt;br /&gt;
  my $hash = $defs{$name};&lt;br /&gt;
  &lt;br /&gt;
  ## Konstanten&lt;br /&gt;
  ###############  &lt;br /&gt;
  use constant {&lt;br /&gt;
    GSPDEF    =&amp;gt; 20,                                                                    # Einstellung des Grid Setpoint&lt;br /&gt;
    FINDEF    =&amp;gt; 5000,                                                                  # Default Einspeiselimit&lt;br /&gt;
    MINSOCDEF =&amp;gt; 10,                                                                    # Default Minimum SoC&lt;br /&gt;
    MAXPLDEF  =&amp;gt; 105,                                                                   # max. Ladestrom (A) Victron MPII &lt;br /&gt;
    SYSVOLTAG =&amp;gt; 48,                                                                    # Batterie Systemspannung&lt;br /&gt;
  };&lt;br /&gt;
  &lt;br /&gt;
  my $batsocmgmt  = AttrVal ($name, &#039;userFn_BatterySoCManagement&#039;,  &#039;Aus&#039;);             # SoC-Management Vorgabe&lt;br /&gt;
  my $batloadmgmt = AttrVal ($name, &#039;userFn_BatteryLoadManagement&#039;, &#039;Aus&#039;);             # Lademanagement Vorgabe&lt;br /&gt;
  my $vicsets     = &#039;MQTT2_cerboGX_c0619ab34e08_settings&#039;;                              # Victron CerboGX Einstellungen&lt;br /&gt;
&lt;br /&gt;
  ## Ladestrategie Ist-Einstellung und Soll-Abgleich&lt;br /&gt;
  ####################################################&lt;br /&gt;
  my $cgbt     = AttrVal ($name, &#039;ctrlBatSocManagement&#039;.$bn, undef);&lt;br /&gt;
  my $strategy = &#039;loadRelease&#039;; &lt;br /&gt;
&lt;br /&gt;
  if ($cgbt) {&lt;br /&gt;
    my $parsed = FHEM::SolarForecast::__parseAttrBatSoc ($name, $cgbt);                 # aktuell gesetzte Strategie ermitteln&lt;br /&gt;
    $strategy  = $parsed-&amp;gt;{loadStrategy} // $strategy;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if ($batloadmgmt ne &#039;Aus&#039; &amp;amp;&amp;amp; $strategy ne $batloadmgmt) {&lt;br /&gt;
    CommandSet (undef, &amp;quot;$name attrKeyVal ctrlBatSocManagement${bn} loadStrategy=$batloadmgmt&amp;quot;);              &lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ## aktuelle Indikatoren&lt;br /&gt;
  ##########################&lt;br /&gt;
  my $surp    = ReadingsNum ($name, &#039;Current_Surplus&#039;,            0);                   # aktueller PV-Überschuß&lt;br /&gt;
  my $bcrq    = ReadingsNum ($name, &#039;Battery_ChargeRequest_&#039;.$bn, 0);                   # Notfallladung&lt;br /&gt;
  my $curgsp  = ReadingsNum ($vicsets, &#039;GridSetpoint&#039;, GSPDEF);                         # aktuelle Einstellung Grid Setpoint&lt;br /&gt;
  my $finplim = FHEM::SolarForecast::CurrentVal ($name, &#039;feedinPowerLimit&#039;,   FINDEF);  # Limit plantControl-&amp;gt;feedinPowerLimit&lt;br /&gt;
  my $preduce = FHEM::SolarForecast::BatteryVal ($name, $bn, &#039;bpinreduced&#039;, MAXPLDEF);  # reduzierte Ladeleistung Bat-Konfig&lt;br /&gt;
  my $actmcc  = ReadingsNum ($vicsets, &#039;MaxChargeCurrent&#039;,     0);                      # akt. maximale Ladestromeinstellung&lt;br /&gt;
  my $actmcp  = ReadingsNum ($vicsets, &#039;MaxChargePower&#039;,   undef);                      # akt. Ladeleistungseinstellung&lt;br /&gt;
  my $load    = MAXPLDEF;                                                               # initialer Soll-Ladestrom (A)&lt;br /&gt;
  &lt;br /&gt;
  my $targetgsp = GSPDEF;                                                               # Voreinstellung Grid Setpoint&lt;br /&gt;
  my $ctype     = &#039;default&#039;;                                                            # Voreinstellung Steuerungstyp  &lt;br /&gt;
&lt;br /&gt;
  ## Battery SoC Management&lt;br /&gt;
  ###########################  &lt;br /&gt;
  if ($batsocmgmt eq &#039;Ein&#039;) { &lt;br /&gt;
    my $csoc = ReadingsNum ($vicsets, &#039;MinimumSocLimit&#039;,               MINSOCDEF);     # akt. SoC&lt;br /&gt;
    my $osoc = ReadingsNum ($name,    &#039;Battery_OptimumTargetSoC_&#039;.$bn, MINSOCDEF);     # optimierter Mindest-SoC&lt;br /&gt;
	&lt;br /&gt;
    if ($csoc != $osoc) {&lt;br /&gt;
      CommandSet (undef, &amp;quot;$vicsets MinimumSocLimit $osoc&amp;quot;);&lt;br /&gt;
      Log3 ($name, 3, qq{$name - userFn SoCMgmnt -&amp;gt; MinimumSocLimit in $vicsets set to $osoc %});&lt;br /&gt;
    }                                       &lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  ## Steuerung nach Ladungsfreigabe&lt;br /&gt;
  ###################################    &lt;br /&gt;
  if ($batloadmgmt eq &#039;loadRelease&#039;) {&lt;br /&gt;
    $ctype         = &#039;loadRelease&#039;;    &lt;br /&gt;
    my $unrestrict = ReadingsNum ($name, &#039;Battery_ChargeUnrestricted_&#039;.$bn, 0);         # Ladefreigabe (1 -&amp;gt; Ladefreigabe)&lt;br /&gt;
	&lt;br /&gt;
    if (!$unrestrict) {                                                                 # Grid Setpoint absenken wenn keine .. &lt;br /&gt;
        $targetgsp    = GSPDEF - $surp;                                                 # ..Batterie Ladefreigabe&lt;br /&gt;
        my $neggspmax = -1 * $finplim;&lt;br /&gt;
		$targetgsp    = $neggspmax if($targetgsp &amp;lt; $neggspmax);                         # Begrenzung der Einspeiseleistung&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if ($curgsp != $targetgsp) {                                                          # GridSetpoint setzen&lt;br /&gt;
	  fhem (&amp;quot;set $vicsets GridSetpoint $targetgsp&amp;quot;);&lt;br /&gt;
      Log3 ($name, 3, qq{$name - userFn ChargeMgmnt &#039;loadRelease&#039; -&amp;gt; GridSetpoint in $vicsets set to $targetgsp});&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  readingsSingleUpdate ($hash, &#039;userFn_Victron_GridSetpoint_set&#039;, $targetgsp, 0); &lt;br /&gt;
  &lt;br /&gt;
  ## Steuerung nach optimaler Ladeleistung&lt;br /&gt;
  ##########################################&lt;br /&gt;
  if ($batloadmgmt =~ /(?:opt|smart)Power/xs) {&lt;br /&gt;
    $ctype  = &#039;optPower&#039;; &lt;br /&gt;
    my $otp = ReadingsNum ($name, &#039;Battery_ChargeOptTargetPower_&#039;.$bn, $preduce);      # optimale Ladeleistung (W)&lt;br /&gt;
    $load   = sprintf &amp;quot;%.0f&amp;quot;, ($otp / SYSVOLTAG);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  ## Anforderungsladung&lt;br /&gt;
  ####################### &lt;br /&gt;
  if ($bcrq) {                                                                         # max. Ladeleistung...&lt;br /&gt;
    $ctype = &#039;requestCharging&#039;;                                                        # bei Battery_ChargeRequest&lt;br /&gt;
    my $p;&lt;br /&gt;
    &lt;br /&gt;
	if ($surp &amp;lt; $preduce) {&lt;br /&gt;
	  $p = $preduce;&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
      $p = ReadingsNum ($name, &#039;Battery_ChargeOptTargetPower_&#039;.$bn, $preduce);         # optimale Ladeleistung (W) &lt;br /&gt;
    }&lt;br /&gt;
	&lt;br /&gt;
    $load = sprintf &amp;quot;%.0f&amp;quot;, ($p / SYSVOLTAG);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  ## Stromeinstellung umsetzen&lt;br /&gt;
  #############################  &lt;br /&gt;
  if ($load != $actmcc) {&lt;br /&gt;
    CommandSet (undef, &amp;quot;$vicsets MaxChargeCurrent $load&amp;quot;);&lt;br /&gt;
	&lt;br /&gt;
    Log3 ($name, 3, qq{$name - userFn ChargeMgmnt &#039;$ctype&#039; -&amp;gt; MaxChargeCurrent in $vicsets set }.&lt;br /&gt;
                    qq{from old $actmcc A to $load A});&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  readingsSingleUpdate ($hash, &#039;userFn_MPII_MaxChargeCurrent_set&#039;, $load, 0);&lt;br /&gt;
  &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
###############################################################################&lt;br /&gt;
# Ende eigener Code&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== sequentielle Batterie-Ladung mehrerer Batterien mit lcSlot ====&lt;br /&gt;
&lt;br /&gt;
Der Parameter &#039;&#039;&#039;ctrlBatSocManagementXX-&amp;gt;lcSlot&#039;&#039;&#039; (&amp;quot;load control Slot&amp;quot;) definiert eine Zeitperiode am aktuellen Tag in der die Steuerungslogik für die Batteriesteuerung aktiviert ist. Ist dieser Parameter nicht gesetzt, ist das eingestellte Lademanagement über den gesamten Tag aktiert und wäre identisch mit der Einstellung:&lt;br /&gt;
&lt;br /&gt;
 lcSlot=00:00-23:59&lt;br /&gt;
&lt;br /&gt;
Somit ist die Ladesteuerung per default über den gesamten Tag aktiv.&lt;br /&gt;
&lt;br /&gt;
Durch das Setzen von lcSlot auf einen täglichen Slot wird die Ladesteuerung auf diese Periode eingegrenzt. In der übrigen Zeit ist die Ladung der Batterie freigegeben (Reading Battery_ChargeUnrestricted_XX) bzw. auf die maximal konfigurierte Ladeleistung eingestellt (Reading Battery_ChargeOptTargetPower_XX). Diese Readings können je nach verwendeter Ladestrategie ausgewertet und zur Steuerung der Batterie(n) verwendet werden.  &lt;br /&gt;
Die SoC-Steuerung wird durch die lcSlot-Angabe nicht beeinflusst.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Mit lcSlot kann zum Beispiel ein sequentielles Laden von mehreren Batterien umgesetzt werden. Nachfolgend wird ein solches Szenario mit drei Batterien  skizziert:&lt;br /&gt;
&lt;br /&gt;
1. die Batterie 1 soll mit voller Leistung geladen werden, die anderen Batterien nur bei weiterem Überschuß. Man würde die jeweiligen &lt;br /&gt;
ctrlBatSocManagementXX-&amp;gt;lcSlot Schlüssel definieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement01 lcSlot=23:00-23:10&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement02 lcSlot=00:00-23:59&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement03 lcSlot=00:00-23:59&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;Wie ist der Ablauf: Die Batterie 1 unterliegt keiner Steuerung (nur von 23:00 bis 23:10 als Dummy-Periode). Das Reading Battery_ChargeUnrestricted_01 wird &amp;quot;1&amp;quot; gesetzt, d.h. die Batterie 1 wird zur Ladung uneingeschränkt freigegeben. Die anderen Batterien unterliegen der Steuerung -&amp;gt; Battery_ChargeUnrestricted_02 / 03 sind &amp;quot;0&amp;quot; und sollen nur geladen werden falls ein gesetztes Einspeiselimit überschritten wird.&lt;br /&gt;
&lt;br /&gt;
Ist das Ladeziel der Batterie 1 erreicht, setzt man um die Batterie 2 vollzuladen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement01 lcSlot=00:00-23:59&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement02 lcSlot=23:00-23:10&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement03 lcSlot=00:00-23:59&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Batterien 1 und 3 unterliegen der Steuerung, d.h. Battery_ChargeUnrestricted_01 / 03 haben den Wert &amp;quot;0&amp;quot;. Demgegenüber ist Battery_ChargeUnrestricted_02=1 und soll uneingeschränkt geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ist auch die Batterie 2 voll geladen, würde man das Laden der Batterie 3 aktivieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement01 lcSlot=00:00-23:59&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement02 lcSlot=00:00-23:59&lt;br /&gt;
set ... attrKeyVal ctrlBatSocManagement03 lcSlot=23:00-23:10&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Setzens via &#039;&#039;&#039;attrKeyVal&#039;&#039;&#039; hat den Vorteil, dass die Zeitfenster-Syntax (Anfangszeit kleiner Endezeit usw.) im Hintergrund peprüft und ggf. ein Fehler zurückgegeben wird, den der Nutzer in seinem Script auswerten kann. Weiterhin werden die Attribute implizit gespeichert sofern global-&amp;gt;autosave nicht explizit auf &amp;quot;0&amp;quot; gesetzt ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Unterstützung eines netzdienlichen Verhaltens ====&lt;br /&gt;
&lt;br /&gt;
Der Steuerungsprozess der Batterieanlage leistet insgesamt einen kleinen Beitrag zur Netzstabilität, indem die Speicherladung in den Zeitraum der meisten prognostizierten Überschußeinspeisung gelegt wird. Im Sommer kann sich dieser Zeitraum in die Zeit nach dem Maxmimum des prognostizierten PV-Überschusses verlagern, wenn auch danach noch genügend Überschußenergie zu Volladung der Batterie prognostiziert wird. In dem Zusammenhang sollte im Attribut &#039;&#039;&#039;plantControl-&amp;gt;feedinPowerLimit&#039;&#039;&#039; ein Einspeiselimit gesetzt werden. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zur Verdeutlichung soll das Beispiel dienen:&lt;br /&gt;
&lt;br /&gt;
* die Batterie ist zu 60% geladen &lt;br /&gt;
* der User hat upSoC=40 eingestellt&lt;br /&gt;
* der User hat maxSoC=90 eingestellt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Am Vormittag ist genügend Sonne vohanden, das Maximumum der Energie über die Zeit wird aber noch erwartet. &lt;br /&gt;
Zu diesem Zeitpunkt wird das Reading  &#039;&#039;&#039;Battery_ChargeUnrestricted_XX&#039;&#039;&#039; auf den Wert &amp;quot;0&amp;quot; gesetzt. Durch den Nutzer erfolgt daraufhin, zum Beipiel über ein Notify, ein anlagenspezifischer Befehl &amp;quot;Ladestop&amp;quot; an die Batterieanlage. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ab diesem Moment wird:&lt;br /&gt;
&lt;br /&gt;
# die Batterie nicht geladen&lt;br /&gt;
# die erzeugte PV-Energie dem Haushalt zur Verfügung gestellt bzw. der verbleibende Überschuß eingespeist, sofern der Überschuß unterhalb des im Attribut &#039;&#039;&#039;plantControl-&amp;gt;feedinPowerLimit&#039;&#039;&#039; definierten Limits liegt.&lt;br /&gt;
# ein eventuell über dem Limit &#039;&#039;&#039;plantControl-&amp;gt;feedinPowerLimit&#039;&#039;&#039; vorhandener Überschuß in die Batterie geladen und so die Netzlast verringert sowie die Batterie schonend geladen. (dieser Punkt trifft in den Monaten mit wenig PV-Erzeugung eher nicht zu)  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Gleichzeitig liefert die Batterie Energie an das Hausnetz und wird von 60% bis maximal einem SoC-Wert entladen, der durch die [[#Die_dynamische_SOC_Steuerung_mit_Umsetzungsbeispiel|dynamische SOC Steuerung]] bestimmt wird. Dazu muß diese Steuerung natürlich implementiert sein. Sofern der berechnete SoC größer als upSoC ist, wird der maximale Entladungs-SoC auf upSoC gesetzt. &lt;br /&gt;
&lt;br /&gt;
Zusätzlich überprüft das Modul permanent bei jedem Zyklus die Differenz des aktuell vorhandenen SoC zum eingestellten maxSoC.&lt;br /&gt;
Sie soll nicht größer sein um mit dem (noch) zu erwartenden PV-überschuß des Tages der eingestellte maxSoC bzw. dessen Nähe wahrscheinlich erreicht werden kann. &lt;br /&gt;
Sollte der Differenz-Grenzwert erreicht sein, wird &#039;&#039;&#039;Battery_ChargeUnrestricted_XX=1&#039;&#039;&#039; gesetzt und der Nutzer sollte dann über einen geeigneten Befehl seine Anlage in den Modus &amp;quot;Laden&amp;quot; umschalten.&lt;br /&gt;
Dieser Vorgang kann beliebig oft während des Tages alternieren.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Nutzung von batteryTrigger ===&lt;br /&gt;
Ergänzt werden die Möglichkeiten durch das set-Kommando: &lt;br /&gt;
&lt;br /&gt;
 batteryTrigger &amp;lt;1on&amp;gt;=&amp;lt;Wert&amp;gt; &amp;lt;1off&amp;gt;=&amp;lt;Wert&amp;gt; [&amp;lt;2on&amp;gt;=&amp;lt;Wert&amp;gt; &amp;lt;2off&amp;gt;=&amp;lt;Wert&amp;gt; ...] &lt;br /&gt;
&lt;br /&gt;
Das Kommando setzt Triggerpunkte bei Über- bzw. Unterschreitung bestimmter Batterieladungswerte (SoC in %).&lt;br /&gt;
&lt;br /&gt;
Der verwendete SoC wird als resultierender SoC als Summe der aktuellen Ladung aller registrierten Batterie-Geräte im Verhältnis zur installierten Gesamtkapazität gebildet. Das bedeutet, dass alle einzelnen Batterie-Geräte als ein gemeinsamer Cluster betrachtet werden. &lt;br /&gt;
&lt;br /&gt;
Es kann eine beliebige Anzahl von Triggerbedingungen angegeben werden. Xon/Xoff-Bedingungen müssen nicht zwingend paarweise definiert werden.&lt;br /&gt;
Überschreiten die letzten drei SoC-Messungen eine definierte Xon-Bedingung, wird das Reading &#039;&#039;&#039;batteryTrigger_X = on&#039;&#039;&#039; erstellt/gesetzt.&lt;br /&gt;
Unterschreiten die letzten drei SoC-Messungen eine definierte Xoff-Bedingung, wird das Reading &#039;&#039;&#039;batteryTrigger_X = off&#039;&#039;&#039; erstellt/gesetzt.&lt;br /&gt;
&lt;br /&gt;
Der Inhalt der Readings kann durch ein User-Programm oder, bei Erzeugung von entsprechenden Events, mittels notify-Device ausgewertet werden um bei Eintreten eines definierten Ereignisses entsprechende Reaktionen auszulösen. Das kann zum Beispiel die Freigabe eines Heizstabes oberhalb eines bestimmten SOC sein oder ein Entladeverbot der Batterie unterhalb eines SOC-Wertes um Reserven für einen eventuellen Stromausfall zu behalten.&lt;br /&gt;
&lt;br /&gt;
Damit die Batterietrigger dynamisch geändert werden können, ist die Einstellung als Set-Kommando und nicht als Attribut ausgeführt.   &lt;br /&gt;
&lt;br /&gt;
Alle Triggerpunkte können mit dem set-Befehl:&lt;br /&gt;
&lt;br /&gt;
 reset batteryTriggerSet&lt;br /&gt;
&lt;br /&gt;
wieder gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
== Verbrauchersteuerung - Registrieren und visualisieren von Verbrauchern ==&lt;br /&gt;
&lt;br /&gt;
Das Modul gestattet es beliebige Verbraucher (Devices) über die Attribute &#039;&#039;&#039;consumerXX&#039;&#039;&#039; zu registrieren. Durch die Registrierung wird dem Modul der Namen des Devices sowie dessen Eingenschaften durch die Angabe von Schlüssel-Wert Paaren bekannt gemacht.&lt;br /&gt;
&lt;br /&gt;
Nach der Registrierung können die Verbraucher durch das Modul genutzt werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* es erfolgt eine Planung der Ein- und Ausschaltzeiten abhängig von der solaren Prognose in Bezug zu den Leistungsdaten des Verbrauchers sowie der anderen registrierten Consumer&lt;br /&gt;
* das Modul kann die Ein- und Ausschaltsteuerung der Consumer übernehmen (optional)&lt;br /&gt;
* die aktuellen Status (Verbrauchsdaten) werden in der Energiefußgrafik dargestellt&lt;br /&gt;
* das Modul lernt mit der Zeit das Verbrauchsverhalten der registrierten Verbraucher&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Um einen Verbaucher zu registrieren, wird ein freies &#039;&#039;&#039;consumerXX&#039;&#039;&#039; Attribut, zum Beispiel consumer01, gesetzt.&lt;br /&gt;
&lt;br /&gt;
Die Eigenschaften und das Schaltverhalten wird durch die Angabe der verfügbaren Schlüssel gesteuert. Die Möglichkeiten sind sehr umfangreich. Die nachfolgenden Abschnitte beschreiben die Verwendung am Beispiel oft vorkommender bzw. nachgefragter Sachverhalte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Die Phasen der Verbrauchersteuerung ===&lt;br /&gt;
&lt;br /&gt;
Jeder Zyklus eines Verbrauchers durchläuft im Modul die folgenden Phasen:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Planung&#039;&#039;&#039; der Aktivzeiten des Verbrauchers, d.h. wann der Verbraucher geplant eingeschaltet sowie geplant ausgeschaltet wird und damit der Zyklus beendet wird.&lt;br /&gt;
# &#039;&#039;&#039;Start&#039;&#039;&#039; des Zyklus, d.h. Einschalten des Verbrauchers entsprechend der Planungsdaten sofern weitere, in den Consumer-Schlüsseln definierbare Rahmenbedingungen das Einschalten nicht verhindern (z.B. kein oder zu wenig PV-Energieerzeugung / PV-Überschuss)&lt;br /&gt;
# &#039;&#039;&#039;Permanente Überprüfung von&#039;&#039;&#039; eventuell vorhandenen &#039;&#039;&#039;Interruptbedingungen&#039;&#039;&#039; die den Verbraucher temporär ausschalten und, sofern die Interruptbedingung nicht mehr vorliegt, den Verbraucher wieder einschalten und so den gestarteten Zyklus fortsetzen.&lt;br /&gt;
#  &#039;&#039;&#039;Beendigung&#039;&#039;&#039; der Zyklus. Die Beendigung erfolgt entweder (normalerweise) wenn die geplante Endezeit der Zyklus erreicht ist oder eine optionale Endebedingung erfüllt ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Steuerung der Phasen ====&lt;br /&gt;
Der Ablauf der Phasen kann durch verschiedene Schlüssel-Wert Paare beeinflusst und gesteuert werden.&lt;br /&gt;
&lt;br /&gt;
Mit dem optionalen Schlüssel &#039;&#039;&#039;swoncond&#039;&#039;&#039; kann eine &#039;&#039;&#039;zusätzliche externe Bedingung&#039;&#039;&#039; definiert werden um den Einschaltvorgang des Consumers freizugeben. Ist die Bedingung (Regex) nicht erfüllt, erfolgt kein Einschalten des Verbrauchers auch wenn die sonstigen Voraussetzungen wie Zeitplanung, on-Schlüssel, auto-Mode und aktuelle PV-Leistung gegeben sind. Es erfolgt somit eine &#039;&#039;&#039;UND-Verknüpfung&#039;&#039;&#039; des Schlüssels swoncond mit den Planungsdaten und weiteren Einschaltbedingungen.&lt;br /&gt;
&lt;br /&gt;
Der optionale Schlüssel &#039;&#039;&#039;swoffcond&#039;&#039;&#039; definiert eine &#039;&#039;&#039;vorrangige Ausschaltbedingung&#039;&#039;&#039; (Regex). Sobald diese Bedingung erfüllt ist, wird der Consumer ausgeschaltet auch wenn die geplante Endezeit (consumerXX_planned_stop) noch nicht erreicht ist (&#039;&#039;&#039;ODER-Verknüpfung&#039;&#039;&#039;). Weitere Bedingungen wie off-Schlüssel und auto-Mode müssen zum automatischen Ausschalten erfüllt sein.&lt;br /&gt;
&lt;br /&gt;
Mit dem optionalen Schlüssel &#039;&#039;&#039;interruptable&#039;&#039;&#039; kann während der geplanten Einschaltzeit eine automatische Unterbrechung sowie Wiedereinschaltung des Verbrauchers vorgenommen werden. Der Verbraucher wird temporär ausgeschaltet (interrupted) und wieder eingeschaltet (continued) wenn die Interrupt-Bedingung nicht mehr vorliegt. Die verbleibende Laufzeit wird durch einen Interrupt nicht beeinflusst! &lt;br /&gt;
&lt;br /&gt;
Der Schlüssel &#039;&#039;&#039;power&#039;&#039;&#039; gibt die nominale Leistungsaufnahme des Verbrauchers gemäß seines Datenblattes an. Dieser Wert wird verwendet um die Schaltzeiten des Verbrauchers zu planen und das Schalten in Abhängigkeit des tatsächlichen PV-Überschusses zum Einplanungszeitpunkt zu steuern. Ist &#039;&#039;&#039;power=0&#039;&#039;&#039; gesetzt, wird der Verbraucher unabhängig von einem zum Zeitpunkt des Zyklus-Start eventuell nicht ausreichend vorhandenem PV-Überschuss wie geplant eingeschaltet und somit der Zyklus gestartet.&lt;br /&gt;
&lt;br /&gt;
Durch die Schlüssel &#039;&#039;&#039;notbefore&#039;&#039;&#039; und &#039;&#039;&#039;notafter&#039;&#039;&#039; kann der Startzeitpunkt des Verbrauchers so gesteuert werden, dass der Start des Zyklus nicht vor bzw. nach der angegebenen Zeit eingeplant wird. Diese Steuerung bezieht sich auf die &#039;&#039;Planung&#039;&#039;, d.h. der eigentliche Start des Verbrauchers kann sich unter Umständen durch vorhandene Rahmenbedingungen verzögern. &lt;br /&gt;
&lt;br /&gt;
Der Schlüssel &#039;&#039;&#039;mintime&#039;&#039;&#039; legt die geplanten Einschaltzeit, d.h. die Laufzeit des Zyklus fest. Der Name &#039;&#039;&#039;mintime&#039;&#039;&#039; ist nicht als MInimum-Zeit zu deuten, sondern beschreibt die angegebene Zeit in Minuten die der Zyklus dauern soll.&lt;br /&gt;
&lt;br /&gt;
Dieser Schlüssel ist optional, da sich die Standard-Laufzeit des Verbrauchers von dessen Typ abgeleitet wird. Diese Standard Laufzeiten sind:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|dryer&lt;br /&gt;
|90 Minten&lt;br /&gt;
|-&lt;br /&gt;
|dishwasher&lt;br /&gt;
|180 Minuten&lt;br /&gt;
|-&lt;br /&gt;
|washingmachine&lt;br /&gt;
|120 Minuten&lt;br /&gt;
|-&lt;br /&gt;
|heater&lt;br /&gt;
|240 Minuten&lt;br /&gt;
|-&lt;br /&gt;
|charger&lt;br /&gt;
|120 Minuten&lt;br /&gt;
|-&lt;br /&gt;
|other&lt;br /&gt;
|60 Minuten&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Der Verbraucher wurde nicht gestartet obwohl die Startzeit der Einplanung erreicht wurde ===&lt;br /&gt;
&lt;br /&gt;
Wenn die Startzeit des Consumers entsprechend der Einplanung erreicht oder überschritten ist, wird der Consumer eingeschaltet wenn die weiteren Voraussetzungen wahr sind:&lt;br /&gt;
&lt;br /&gt;
* es ist aktuell ein PV-Überschuss vorhanden und der PV-Überschuss deckt die kalkulierten Leistungsaufnahme des Verbrauchers ab. Mit den Schlüsseln &#039;&#039;&#039;power&#039;&#039;&#039; und &#039;&#039;&#039;spignorecond&#039;&#039;&#039; kann die Einschaltung trotz fehlendem PV-Überschuss ermöglicht werden. &lt;br /&gt;
&lt;br /&gt;
* die Angabe im Schlüssel &#039;&#039;&#039;swoncond&#039;&#039;&#039; ist wahr (sofern gesetzt)&lt;br /&gt;
&lt;br /&gt;
* das Schalten ist über den Schlüssel &#039;&#039;&#039;auto&#039;&#039;&#039; freigegeben (sofern gesetzt)&lt;br /&gt;
&lt;br /&gt;
* im Schlüssel &#039;&#039;&#039;on&#039;&#039;&#039; ist ein valides Einschaltkommando hinterlegt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sind diese zusätzlichen Bedingungen nicht erfüllt, erfolgt kein Start des eingeplanten Zyklus. Der Verbraucher verbleibt in diesem Fall weiterhin im Status &#039;&#039;planned&#039;&#039; oder &#039;&#039;suspended&#039;&#039;. Die Startbedingungen werden regelmäßig reviewed und der Consumer gestartet sobald alle Startbedingungen wahr sind bzw. Start verhindernde Bedingungen entfallen sind. &lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang erfolgt bis das geplante Ende des Verbaucherzyklus erreicht ist oder die Bedingung im Schlüssel &#039;&#039;&#039;swoffcond&#039;&#039;&#039; zutrifft. Sobald die Bedingung im Schlüssel &#039;&#039;&#039;swoffcond&#039;&#039;&#039; wahr ist, wird der gesamte Planungszyklus des Consumers beendet.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Kann manuell / automatisiert in den Planungszyklus eines Verbrauchers eingegriffen werden? ===&lt;br /&gt;
&lt;br /&gt;
Die Einplanung und die darauf aufbauenden Schaltprozesse laufen gemäß den hinterlegten Schlüsseloptionen im Modul automatatisch ab ohne dass ein Eingriff durch den Anwender erfolgen muß.&lt;br /&gt;
&lt;br /&gt;
Dennoch kann es gewünscht sein eine Neuplanung oder eine Sofortplanung eines Consumers vorzunehmen weil der Bedarf dazu besteht. Zu diesem Zweck stehen die Set-Kommandos&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* consumerNewPlanning &lt;br /&gt;
&lt;br /&gt;
* consumerImmediatePlanning &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
zur Verfügung. &lt;br /&gt;
Beide Befehle initiieren die Consumer-Neuplanung, wobei mit dem Befehl &#039;&#039;consumerImmediatePlanning&#039;&#039; die eventuell im consumerXX Attribut gesetzten Schlüssel notbefore, notafter bzw. mode werden nicht beachtet werden. Dagenen beachtet der Befehl &#039;&#039;consumerNewPlanning&#039;&#039; alle definierten Schlüsseloptionen und dient gewöhnlich dazu einen abgeschlossenen Verbraucherzyklus wiederholt ausführen zu lassen weil die aktuelle Witterungslage dazu geeignet ist. &lt;br /&gt;
&lt;br /&gt;
Ein Anwendungsbeispiel dafür ist zum Beispiel die Ausführung von mehreren Waschmaschinen-Zyklen an einem langen, sonnigen Sommertag.&lt;br /&gt;
Diese beiden Set-Befehle können zum Beispiel über ein auf einen Schalter/Taster reagierendes Notify oder DOIF Device ausgeführt werden. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung eines Verbrauchers mit getrennten Geräten für Messung und Schalten ===&lt;br /&gt;
&lt;br /&gt;
Es existieren in FHEM Geräte, die über verschiedene Kanäle für unterschiedliche Aufgaben verfügen. Diese Kanäle werden jeweils in einem gesonderten Device abgebildet. HomeMatic oder readingsProxy sind Beispiele für solche Varianten.&lt;br /&gt;
&lt;br /&gt;
Um solche Verbraucher einzubinden gibt es im consumerXX Attribut den Schlüssel &#039;&#039;&#039;switchdev&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei der Registrierung gibt man als Consumerdevice das Gerät für die Messung an und im Schüssel switchdev das entsprechende FHEM-Device, welches für die Schaltvorgänge benutzt wird. &lt;br /&gt;
Ist switchdev angegeben, beziehen sich die weiteren Schlüssel on, off, swstate, auto und asynchron auf dieses Gerät.&lt;br /&gt;
&lt;br /&gt;
In dem nachfolgenden Beispiel (Homematic) ist &#039;&#039;eg.az.fridge_Pwr&#039;&#039; FHEM Device für die Energiemessung. Die Schlüssel &#039;&#039;pcurr&#039;&#039; und &#039;&#039;etotal&#039;&#039; geben Readings in diesem Device an damit SolarForecast den Verbrauch und die Energiemengen auslesen kann. Demgegenüber ist im Schlüssel &#039;&#039;switchdev&#039;&#039; das zugehörige Schalter-Device der Kombination angegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
eg.az.fridge_Pwr&lt;br /&gt;
type=noSchedule switchdev=eg.az.fridge_Sw power=0 icon=fridge pcurr=power:W:5 etotal=energyCalc:Wh &lt;br /&gt;
swstate=state:on:off auto=automatic&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung von Verbrauchern ohne Zeitplanung und aktivem Schalten ===&lt;br /&gt;
Manchmal besteht der Wunsch, Verbraucher in SolarForecast zu integrieren um lediglich folgende Sachverhalte abzubilden:&lt;br /&gt;
&lt;br /&gt;
* Visualisierung des Schaltstatus in der Consumer Legende und/oder der Energieflußgrafik&lt;br /&gt;
* Darstellung des aktuellen Energieverbrauchs in der Energieflußgrafik&lt;br /&gt;
* Erfassung der (externen) Schaltzeiten, d.h. der Zeiten der Gerätenutzung sowie der Verbrauchsdaten um deren Einfluß bei den Planungszeiten anderer Geräte durch SolarForecast berücksichtigen zu lassen&lt;br /&gt;
&lt;br /&gt;
In solchen Fällen werden bei der Registrierung die Schlüssel &#039;&#039;swstate, pcurr&#039;&#039; und &#039;&#039;etotal&#039;&#039; zur Erfassung der Schaltzustände und der Energieverbrauchs angegeben, jedoch zur Verhinderung der Zeitplanung durch SolarForecast der Schlüssel &#039;&#039;&#039;type=noSchedule&#039;&#039;&#039; gesetzt sowie die Schlüssel &#039;&#039;on&#039;&#039; und &#039;&#039;off&#039;&#039; &#039;&#039;&#039;nicht&#039;&#039;&#039; gesetzt.&lt;br /&gt;
&lt;br /&gt;
Das nachfolgende Beispiel zeigt eine mögliche Registrierung eines Zwischensteckers (Shelly) zur Erfassung der Daten eines Gefrierschrankes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Shelly.shellyplug4&lt;br /&gt;
type=noSchedule power=65 asynchron=1&lt;br /&gt;
icon=gefrierschrank &lt;br /&gt;
swstate=state:.*on.*:.*off.* &lt;br /&gt;
pcurr=relay_0_power:W:5 etotal=relay_0_energy_Wh:Wh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Beispiel Registrierung eines Shelly Devices ===&lt;br /&gt;
&lt;br /&gt;
Nachfolgendes Beispiel zeigt eine Registrierung eines Heizlüfters der an einem Shelly Zwischenstecker angeschlossen ist und durch das Modul gesteuert werden soll. Dabei soll die Steuerung nicht nur von der Solarprognose, sondern auch von der Raumtemperatur abhängig erfolgen. Im Beispiel wird das Attribut consumer03 verwendet.&lt;br /&gt;
&lt;br /&gt;
Zwingende Angaben sind&lt;br /&gt;
&lt;br /&gt;
 consumerXX &amp;lt;Device Name&amp;gt; type=&amp;lt;type&amp;gt; power=&amp;lt;power&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Alle Angaben können mehrzeilig eingegeben werden. Die Schlüssel-Werte Paare sind jeweils durch ein Leerzeichen getrennt.&lt;br /&gt;
&lt;br /&gt;
 consumerXX Shelly.shellyplug3 type=heater power=1700 &lt;br /&gt;
&lt;br /&gt;
In dem Beispiel ist Shelly.shellyplug3 der Devicename des Shellies in FHEM. Der Schlüssel &#039;&#039;&#039;type&#039;&#039;&#039; definiert die Art des Verbrauchers. In der Hilfe sind die möglichen Typen aufgeführt. Den richtigen Typ anzugeben hat Einfluß auf die spätere Einschätzung des Leistungsverhaltens über die Laufzeit. So wird von einem &#039;&#039;&#039;heater&#039;&#039;&#039; gleich nach dem Einschalten die angegebene Nominalleistung abgerufen und wird über die Zeit gleichbleiben. &lt;br /&gt;
Eine Waschmaschine oder ein Trockner rufen über ihre Laufzeit die Leistung nicht gleichbleibend ab und ändern die Leistungsaufnahme sich über die Einschaltdauer.&lt;br /&gt;
&lt;br /&gt;
Die Angabe von &#039;&#039;&#039;power&#039;&#039;&#039; definiert die Nominalleistung (Watt) die für den Verbraucher laut Typenschild vom Hersteller angegeben wird. Diese Angabe wird vom Modul bei der Planung der Einschaltzeiten verwendet indem die Nominalleistung in das Verhältnis zur Solarprognose bzw. Verbrauchsprognose des Netzes gesetzt wird. Weiterhin ist dieser Wert auch wichtig um später den tatsächlichen Einschaltzeitpunkt auszuführen wenn ein realer PV Überschuss festgestellt wird.&lt;br /&gt;
&lt;br /&gt;
Es ist möglich &#039;&#039;&#039;power=0&#039;&#039;&#039; zu setzen. Das führt dazu, dass die Planung und letztendlich auch der Schaltvorgang unabhängig von der Solarprognose bzw. einem realen PV Überschuss vorgenommen wird.  &lt;br /&gt;
&lt;br /&gt;
Es werden weitere Schlüsseleingaben vorgenommen:&lt;br /&gt;
&lt;br /&gt;
 Shelly.shellyplug3 type=heater power=1700 icon=vent_ventilation mode=can notbefore=09 mintime=SunPath:60:-60 on=on off=off&lt;br /&gt;
&lt;br /&gt;
Mit dem Schlüssel &#039;&#039;&#039;icon&#039;&#039;&#039; legt man ein Icon fest welches in der Grafik für den Verbraucher verwendet wird. Der &#039;&#039;&#039;mode&#039;&#039;&#039; definiert die Art und Weise der Einplanung:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;can: Die Einplanung erfolgt zum Zeitpunkt mit wahrscheinlich genügend verfügbaren PV Überschuss. Der Start des Verbrauchers zum Planungszeitpunkt unterbleibt bei ungenügendem PV-Überschuss.&lt;br /&gt;
&lt;br /&gt;
;must: Der Verbaucher wird optimiert eingeplant auch wenn wahrscheinlich nicht genügend PV Überschuss vorhanden sein wird. Der Start des Verbrauchers erfolgt auch bei ungenügendem PV-Überschuss.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;notbefore&#039;&#039;&#039; wird festgelegt, dass die Einplanung nicht vor neun Uhr morgens erfolgen soll auch falls schon genügend PV Überschuss vorhanden wäre. Der Schlüssel &#039;&#039;&#039;mintime&#039;&#039;&#039; definiert die Einplanungsdauer des Verbrauchers in Minuten im einfachsten Fall. Die hier verwendete Angabe &#039;&#039;&#039;SunPath&#039;&#039;&#039; ist ein Spezialfall. Der Verbraucher soll von Sonnenaufgang bis Sonnenuntergang eingeschaltet werden, wobei die Angabe von 60 bzw. -60 ein relative Verschiebung bewirken. Dadurch wird das Einschalten 60 Mintuen nach Sonnenaufgang bis 60 Minuten vor Sonnenuntergang geplant.&lt;br /&gt;
&lt;br /&gt;
Die Schlüssel &#039;&#039;&#039;on&#039;&#039;&#039; und &#039;&#039;&#039;off&#039;&#039;&#039; teilen dem Modul die jeweiligen gültigen Ein- und Aus-Kommandos mit, mit dem das (Shelly)Device geschaltet werden kann. Werden diese Schlüssel nicht oder &amp;quot;leer&amp;quot; (on= off=) angegeben, erfolgt kein Schalten durch das Modul, nur die Planungsdaten werden erzeugt.&lt;br /&gt;
&lt;br /&gt;
Die Verbraucherregistrierung wird mit weiteren Angaben ergänzt um das gewünschte Verhalten zu erreichen:&lt;br /&gt;
&lt;br /&gt;
 Shelly.shellyplug3 type=heater power=1700 icon=vent_ventilation mode=can notbefore=09 mintime=SunPath:60:-60 on=on off=off etotal=relay_0_energy_Wh:Wh  &lt;br /&gt;
 pcurr=relay_0_power:W auto=automatic interruptable=og.bad.wandthermostat:diff-temp:[0-9]\.[0-9]:0.2&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;&#039;etotal&#039;&#039;&#039; wird der Readingname des Shelly Devices angegeben welches die Summe der verbrauchten Energie (Wh/kWh) des Consumer Device liefert. D.h. es muß ein sich stetig erhöhender Wert sein. Durch die Auswertung dieses Readings ermittelt das Modul die in bestimmten Zeiteinheiten verbrauchte Energie zur weiteren Verwendung. &lt;br /&gt;
&lt;br /&gt;
In dem Shelly Device ist per default ein solches Reading nicht vorhanden. Über userReadings kann in dem Device Shelly.shellyplug3 ein Reading für etotal erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
 userReadings relay_0_energy_Wh:relay_0_energy.* monotonic { sprintf &amp;quot;%.0f&amp;quot;, ReadingsVal ($name, &#039;relay_0_energy&#039;, 0) / 60 } &lt;br /&gt;
&lt;br /&gt;
Der Schlüssel &#039;&#039;&#039;pcurr&#039;&#039;&#039; enthält das Reading in Shelly.shellyplug3 welches den aktuellen Energieverbrauch liefert. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;auto&#039;&#039;&#039; enthält das Reading in Shelly.shellyplug3 welches zur Freigabe/Sperrung der autmatischen Schaltung durch das Modul dienen soll. Ist das angegebene Reading (im Beispiel &amp;quot;automatic&amp;quot;) im Shelly.shellyplug3 nicht vorhanden, wird es vom Modul automatisch mit dem Wert &amp;quot;1&amp;quot; angelegt.&lt;br /&gt;
Dadurch ist per default das automatische Schalten von Shelly.shellyplug3 durch das Modul freigegeben. &lt;br /&gt;
Der User kann durch Setzen des Readings &#039;&#039;&#039;automatic=0&#039;&#039;&#039; das automatische Schalten durch das Modul sperren und mit &amp;quot;1&amp;quot; wieder freigeben. Dadurch kann man zu bestimmten Zeiten (Urlaub, Feiertage, etc.) die Schaltung temporär deaktivieren.&lt;br /&gt;
&lt;br /&gt;
Wie oben beschrieben, soll der Heizlüfter als weitere Schaltbedingung die Abhängigkeit von der Raumtemperatur beachten. Konkret soll der Heizlüfter mit einer Hysterese von 0.2 (Grad) bei Erreichen einer Soll-Raumtemperatur ausschalten und bei Unterschreiten einer bestimmten Temperatur einschalten.&lt;br /&gt;
Der Schlüssel &#039;&#039;&#039;interruptable&#039;&#039;&#039; übernimmt diese temporäre Schaltsequenzen.&lt;br /&gt;
&lt;br /&gt;
Zunächst wird in dem Sensordevice (In dem Beispiel ein Homatic Wandthermostat HM-TC-IT-WM-W-EU) ein Reading erstellt welches dann im Schlüssel &#039;&#039;&#039;interruptable&#039;&#039;&#039; angegeben wird. Dieses Reading soll einen Wert enthalten auf den der angegebene Regex ([0-9]\.[0-9]) matchen soll um Shelly.shellyplug3 temporär auszuschalten.&lt;br /&gt;
Im Wandthermostat wird dazu ein userReading angelegt:&lt;br /&gt;
&lt;br /&gt;
 userReadings diff-temp:desired-temp.* { &lt;br /&gt;
     sprintf &amp;quot;%.1f&amp;quot;, ReadingsVal ($name, &#039;measured-temp&#039;, 0) - ReadingsVal ($name, &#039;desired-temp&#039;, 0) &lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das bedeutet, wenn die Raumtemperatur die Solltemperatur erreicht oder höher ist, wird diff-temp &amp;gt;= 0.&lt;br /&gt;
In diesem Fall matcht der angebene Regex in:&lt;br /&gt;
&lt;br /&gt;
 interruptable=og.bad.wandthermostat:diff-temp:&#039;&#039;&#039;[0-9]\.[0-9]&#039;&#039;&#039;:0.2&lt;br /&gt;
&lt;br /&gt;
und Shelly.shellyplug3 wird ausgeschaltet. &lt;br /&gt;
Unterschreitet der Wert von diff-temp 0, matcht der Regex nicht mehr und der Verbraucher wird wieder eingeschaltet. Dabei wird die angegebene Hysterese berücksichtigt, d.h der Verbraucher wird erst ausgeschaltet wenn &amp;quot;diff-temp - 0.2 &amp;gt;= 0&amp;quot; wahr ist.&lt;br /&gt;
&lt;br /&gt;
Für das Wiedereinschalten des Heizlüfters ist außerdem Voraussetzung, dass ein entsprechender PV-Überschuss vorliegt. Diese Bedingung wird durch die Angabe von &#039;&#039;&#039;power=1700&#039;&#039;&#039; bewirkt. Soll der zwangsweise PV Überschuss ignoriert werden, kann &#039;&#039;&#039;power=0&#039;&#039;&#039; angegeben werden. Alternativ kann die Berücksichtigung des zwangsweisen PV Überschuss mit dem Schlüssel &amp;quot;spignorecond&amp;quot; im Consumer-Attribut ausgesteuert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== zusätzliche Readings bzw. Erzeugung von statistischen Readings ==&lt;br /&gt;
&lt;br /&gt;
Zusätzlich zu den zahlreichen im Modul per Standard erzeugten Readings kann der Anwender weitere Readings generieren lassen.&lt;br /&gt;
Diese zusätzlichen Readings geben entweder einen bestimmten Sachverhalt (Laufzeit, Status der API-Abfragen, etc.) oder einen statistischen Wert, z.B. die Batterieentladung des aktuellen Tages, wieder.&lt;br /&gt;
&lt;br /&gt;
Die zusätzlich erstellten Readings sind durch den Präfix &#039;&#039;&#039;special_&#039;&#039;&#039; im Device gekennzeichnet.&lt;br /&gt;
&lt;br /&gt;
Diese zusätzlichen Informationen können durch das Setzen einer oder mehrerer Kennzeichen im Attribut &#039;&#039;&#039;ctrlSpecialReadings&#039;&#039;&#039; erzeugt werden. Die mögliche Auswahl wird je nach Bedarf ausgebaut und umfasst zum aktuellen Zeitpunkt (07.02.2025) diese Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;BatPowerIn_Sum: 	die Summe der momentanen Batterieladeleistung aller definierten Batterie Geräte&lt;br /&gt;
;BatPowerOut_Sum: 	die Summe der momentanen Batterieentladeleistung aller definierten Batterie Geräte&lt;br /&gt;
;allStringsFullfilled: 	Erfüllungsstatus der fehlerfreien Generierung aller Strings&lt;br /&gt;
;conForecastTillNextSunrise: 	Verbrauchsprognose von aktueller Stunde bis zum kommenden Sonnenaufgang&lt;br /&gt;
;currentAPIinterval: 	das aktuelle Abrufintervall der gewählten Strahlungsdaten-API in Sekunden&lt;br /&gt;
;currentRunMtsConsumer_XX: 	die Laufzeit (Minuten) des Verbrauchers &amp;quot;XX&amp;quot; seit dem letzten Einschalten. (letzter Laufzyklus)&lt;br /&gt;
;dayAfterTomorrowPVforecast: 	liefert die Vorhersage der PV Erzeugung für Übermorgen (sofern verfügbar) ohne Autokorrektur (Rohdaten).&lt;br /&gt;
;daysUntilBatteryCare_XX: 	Tage bis zur nächsten Batterie XX Pflege (Erreichen der Ladung &#039;maxSoC&#039; aus Attribut ctrlBatSocManagementXX)&lt;br /&gt;
;lastretrieval_time: 	der letzte Abrufzeitpunkt der gewählten Strahlungsdaten-API&lt;br /&gt;
;lastretrieval_timestamp: 	der Timestamp der letzen Abrufzeitpunkt der gewählten Strahlungsdaten-API&lt;br /&gt;
;response_message: 	die letzte Statusmeldung der gewählten Strahlungsdaten-API&lt;br /&gt;
;runTimeAvgDayConsumer_XX: 	die durchschnittliche Laufzeit (Minuten) des Verbrauchers &amp;quot;XX&amp;quot; an einem Tag&lt;br /&gt;
;runTimeCentralTask: 	die Laufzeit des letzten SolarForecast Intervalls (Gesamtprozess) in Sekunden&lt;br /&gt;
;runTimeTrainAI: 	die Laufzeit des letzten KI Trainingszyklus in Sekunden&lt;br /&gt;
;runTimeLastAPIAnswer: 	die letzte Antwortzeit des Strahlungsdaten-API Abrufs auf einen Request in Sekunden&lt;br /&gt;
;runTimeLastAPIProc: 	die letzte Prozesszeit zur Verarbeitung der empfangenen Strahlungsdaten-API Daten&lt;br /&gt;
;SunMinutes_Remain: 	die verbleibenden Minuten bis Sonnenuntergang des aktuellen Tages&lt;br /&gt;
;SunHours_Remain: 	die verbleibenden Stunden bis Sonnenuntergang des aktuellen Tages&lt;br /&gt;
;todayConsumptionForecast: 	Verbrauchsprognose pro Stunde des aktuellen Tages (01-24)&lt;br /&gt;
;todayConForecastTillSunset: 	Verbrauchsprognose von aktueller Stunde bis Stunde vor Sonnenuntergang&lt;br /&gt;
;todayDoneAPIcalls: 	die Anzahl der am aktuellen Tag ausgeführten Strahlungsdaten-API Calls&lt;br /&gt;
;todayDoneAPIrequests: 	die Anzahl der am aktuellen Tag ausgeführten Strahlungsdaten-API Requests&lt;br /&gt;
;todayConsumption: 	der gesamte Energieverbrauch (Wh) des Hauses am aktuellen Tag&lt;br /&gt;
;todayGridConsumption: 	die aus dem öffentlichen Netz bezogene Energie am aktuellen Tag&lt;br /&gt;
;todayGridFeedIn: 	die in das öffentliche Netz eingespeiste PV Energie am aktuellen Tag&lt;br /&gt;
;todayMaxAPIcalls: 	die maximal mögliche Anzahl Strahlungsdaten-API Calls. Ein Call kann mehrere API Requests enthalten.&lt;br /&gt;
;todayRemainingAPIcalls: 	die Anzahl der am aktuellen Tag noch möglichen Strahlungsdaten-API Calls&lt;br /&gt;
;todayRemainingAPIrequests: 	die Anzahl der am aktuellen Tag noch möglichen Strahlungsdaten-API Requests&lt;br /&gt;
;todayBatIn_XX: 	die am aktuellen Tag in die Batterie XX geladene Energie&lt;br /&gt;
;todayBatInSum: 	Summe der am aktuellen Tag in alle Batterien geladene Energie&lt;br /&gt;
;todayBatOut_XX: 	die am aktuellen Tag aus der Batterie XX entnommene Energie&lt;br /&gt;
;todayBatOutSum: 	Summe der am aktuellen Tag aus allen Batterien entnommene Energie&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Manche Angaben sind im Setup des Moduls optional, d.h. sie müssen nicht zwingend beim Setup angegeben werden. Allerdings sind diese Angaben für die Erstellung eines zusätzlichen Readings unter Umständen Voraussetzung. Es empfiehlt sich deshalb bei Setup so viele Informationen wie möglich zu übermitteln.&lt;br /&gt;
&lt;br /&gt;
So ist zum Beispiel für die sinnvolle Erstellung der Informationen &#039;&#039;&#039;todayBatInSum&#039;&#039;&#039; und &#039;&#039;&#039;todayBatOutSum&#039;&#039;&#039; (Readings special_todayBatInSum und special_todayBatOutSum) das Vorhandensein der Schlüssel &#039;&#039;intotal&#039;&#039; und &#039;&#039;outtotal&#039;&#039; im Attribut setupBatteryDevXX notwendig (Beispiel):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;Name&amp;gt; setupBatteryDev01 MQTT2_cerboGX_c0619ab34e08_battery &lt;br /&gt;
                              pin=BatIn:W pout=BatOut:W charge=SOC_value intotal=BatInTotal:Wh outtotal=BatOutTotal:Wh &lt;br /&gt;
                              cap=InstalledCapacity_Wh:Wh asynchron=1 show=1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hinweise zur Fehlersuche ==&lt;br /&gt;
Das Modul SolarForecast ist sehr komplex und es die verschiedensten Datenquellen ausgewertet, zusammengeführt und daraus Ergebnisse berechnet und/oder visualisiert. Wird etwas nicht wie erwartet durch das Modul geliefert, können die Ursachen vielfältig sein. Natürlich kann ein Fehler im Modulcode vorliegen, oftmals gibt es aber andere Ursachen.&lt;br /&gt;
&lt;br /&gt;
Die nachfolgenden Hinweise sollen Hilfe zur Selbsthilfe geben und sind eine Zusammenfassung von realen Fällen die mit dem beschriebenen Wegen und Verfahren gelöst wurden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Die erwartete erzeugte PV-Energie ist im Modul zu gering und wird im Balkendiagramm nicht angezeigt === &lt;br /&gt;
&lt;br /&gt;
Die erzeugte PV-Energie wird nicht richtig angezeigt: Am Vormittag sind keine Werte vorhanden und, wenn dann etwas kommt, ist die Anzeige zu gering. Für 13 und 14 Uhr wurden ca. 5.000Wh erwartet (im Vergleich mit den Daten aus einer anderen Auswertungsquelle). &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Lösungsweg:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Aus dem List des Devices wurden die für den Fall wesentlichen Readings herausgezogen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    2025-02-02 08:59:55  Today_Hour09_PVforecast 152 Wh&lt;br /&gt;
    2025-02-02 08:59:55  Today_Hour09_PVreal 0 Wh&lt;br /&gt;
&lt;br /&gt;
    2025-02-02 09:59:55  Today_Hour10_PVforecast 1400 Wh&lt;br /&gt;
    2025-02-02 09:59:55  Today_Hour10_PVreal 0 Wh&lt;br /&gt;
&lt;br /&gt;
    2025-02-02 10:59:55  Today_Hour11_PVforecast 2500 Wh&lt;br /&gt;
    2025-02-02 10:59:55  Today_Hour11_PVreal 0 Wh&lt;br /&gt;
&lt;br /&gt;
    2025-02-02 11:59:55  Today_Hour12_PVforecast 4392 Wh&lt;br /&gt;
    2025-02-02 11:59:55  Today_Hour12_PVreal 0 Wh&lt;br /&gt;
&lt;br /&gt;
    2025-02-02 12:59:54  Today_Hour13_PVforecast 4000 Wh&lt;br /&gt;
    2025-02-02 12:59:54  Today_Hour13_PVreal 300 Wh&lt;br /&gt;
&lt;br /&gt;
    2025-02-02 13:59:54  Today_Hour14_PVforecast 6415 Wh&lt;br /&gt;
    2025-02-02 13:59:54  Today_Hour14_PVreal 3200 Wh&lt;br /&gt;
&lt;br /&gt;
    2025-02-02 14:59:54  Today_Hour15_PVforecast 5629 Wh&lt;br /&gt;
    2025-02-02 14:59:54  Today_Hour15_PVreal 2700 Wh&lt;br /&gt;
&lt;br /&gt;
    2025-02-02 15:59:54  Today_Hour16_PVforecast 3958 Wh&lt;br /&gt;
    2025-02-02 15:59:54  Today_Hour16_PVreal 900 Wh&lt;br /&gt;
&lt;br /&gt;
    2025-02-02 16:31:24  Today_Hour17_PVforecast 945 Wh&lt;br /&gt;
    2025-02-02 16:31:24  Today_Hour17_PVreal 500 Wh&lt;br /&gt;
   &lt;br /&gt;
    2025-02-02 16:31:24  Today_Hour18_PVforecast 59 Wh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man sieht dass bis 12:00 keine erzeugte Energie vom WR gemeldet wird. Deswegen wird in der Balkengrafik nichts dargestellt. Auch danach bleibt die registrierte PV-Energie hinter den Erwartungen zurück. Zuständig für die Lieferung der PV-Energie an das Modul ist das Reading im Schlüssel:&lt;br /&gt;
&lt;br /&gt;
 setupInverterDev01 SH10rt pv=Total_DC_Power:W &amp;lt;mark&amp;gt;etotal=Total_Export_Energy_from_PV:kWh&amp;lt;/mark&amp;gt; capacity=10000 strings=Hauptdach,Flachdach&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Welche Änderungen es zwischen den Stunden gab, sieht man mit einem &amp;quot;get ... pvHistory XX&amp;quot; (&amp;quot;XX&amp;quot; steht für das Datum, d.h. den Tag, z.B. 02). &amp;lt;br&amp;gt;&lt;br /&gt;
Hier ein Beispiel für die Stunden 10 (9:00 - 9:59) und 11 (10:00 - 10:59). &amp;lt;br&amp;gt;&lt;br /&gt;
Wichtig ist hier der Key etotali01 für den ersten Inverter. Die Differenz zwischen den Werten beider Stunden ist die in der Stunde erzeugte Energie (hier 380 Wh). Diese 380 Wh findest du auch im Key pvrl01 als PVReal für diese Stunde. Das Modul rechnet intern immer mit Wh, kWh werden vorher in Wh umgerechnet.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
10 =&amp;gt; pvfc: 365, pvrl: 530, pvrlvd: 1, rad1h: -&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;mark&amp;gt;etotali01: 63364714&amp;lt;/mark&amp;gt;, etotali02: 3044110, etotali03: -&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;mark&amp;gt;pvrl01: 380&amp;lt;/mark&amp;gt;, pvrl02: 150, pvrl03: -&lt;br /&gt;
&lt;br /&gt;
            etotalp01: -, etotalp02: -, etotalp03: -&lt;br /&gt;
&lt;br /&gt;
            pprl01: -, pprl02: -, pprl03: -&lt;br /&gt;
&lt;br /&gt;
            confc: 574, con: 518, gcons: 22, conprice: 0.2958&lt;br /&gt;
&lt;br /&gt;
            gfeedin: 1, feedprice: 0.1269&lt;br /&gt;
&lt;br /&gt;
            DoN: 1, sunaz: 137, sunalt: 12&lt;br /&gt;
&lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
            &lt;br /&gt;
&lt;br /&gt;
      11 =&amp;gt; pvfc: 2966, pvrl: 1862, pvrlvd: 1, rad1h: -&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;mark&amp;gt;etotali01: 63365094&amp;lt;/mark&amp;gt;, etotali02: 3044260, etotali03: -&lt;br /&gt;
&lt;br /&gt;
            pvrl01: 1392, pvrl02: 470, pvrl03: -&lt;br /&gt;
&lt;br /&gt;
            etotalp01: -, etotalp02: -, etotalp03: -&lt;br /&gt;
&lt;br /&gt;
            pprl01: -, pprl02: -, pprl03: -&lt;br /&gt;
&lt;br /&gt;
            confc: 774, con: 609, gcons: 25, conprice: 0.2958&lt;br /&gt;
&lt;br /&gt;
            gfeedin: 4, feedprice: 0.1269&lt;br /&gt;
           &lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
D.h. wenn zwischen den beiden Stunden keine Differenzen zu finden sind, liegt es sehr wahrscheinlich am Input des Readings:&lt;br /&gt;
&lt;br /&gt;
 etotal=Total_Export_Energy_from_PV:kWh&lt;br /&gt;
&lt;br /&gt;
welches sich in den ersten Stunden nicht ändert und danach auch nur wenig.&lt;br /&gt;
&lt;br /&gt;
Man kann mit dem Attribut:&lt;br /&gt;
&lt;br /&gt;
 ctrlDebug=collectData&lt;br /&gt;
&lt;br /&gt;
die Datensammlung verfolgen (leider entstehen viele Daten im Log). Man sieht welche Werte von dem Inverter/Reading geliefert werden:&lt;br /&gt;
 &lt;br /&gt;
 ....&lt;br /&gt;
 2025.02.02 19:10:10.558 1: SolCast DEBUG&amp;gt; collect Inverter 01 data - device: STP_5000, delivery: default =&amp;gt;&lt;br /&gt;
 2025.02.02 19:10:10.558 1: SolCast DEBUG&amp;gt; pv: 0 W, &amp;lt;mark&amp;gt;etotal: 63378810 Wh&amp;lt;/mark&amp;gt;&lt;br /&gt;
 ....&lt;br /&gt;
&lt;br /&gt;
Dieser Wert muß bei PV Erzeugung kontinuierlich hochzählen. Wenn nicht, muss das angegebene Reading, d.h. die Quelle überprüft werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ergebnis der Prüfung:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Tatsächlich wurde ein Reading benutzt, das ungeeignet war: Statt &amp;quot;Total_PV_Generation&amp;quot; wurde &amp;quot;Total_export_energy_from_PV&amp;quot; genutzt. In diesem Reading wurde immer die in die Batterie gespeicherte Energie von der gesamten erzeugten Energie abgezogen. Daher war auch vormittags alles 0, da in dieser Periode alle PV-Energie in die Batterie geladen wurde bzw. in weiteren Stunden ein Anteil der dann bei dem PV-Erzeugungswert fehlte.&lt;br /&gt;
&lt;br /&gt;
Nachdem die Ursache beseitigt war, zeigt SolarForecast wieder die erwarteten PV-Erzeugungswerte an.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
== Konfigurationsbeispiele ==&lt;br /&gt;
&lt;br /&gt;
=== Visualisierung solare Vorhersage und reale Erzeugung ===&lt;br /&gt;
&lt;br /&gt;
Zu Beginn jedes neuen Tages gegen 00:00 Uhr werden durch das SolarForecast Device Events für die initiale PV Prognose des kommenden Tages erstellt.&lt;br /&gt;
Der Readingteil dieser Events heißt&lt;br /&gt;
&lt;br /&gt;
 AllPVforecastsToEvent&lt;br /&gt;
&lt;br /&gt;
Die Uhrzeiten der Events sind so aufbereitet und können direkt geloggt werden um sie in einen SVG Plot zu integrieren.&lt;br /&gt;
Im SolarForecast Device ist dieses Reading nicht sichtbar, nur die Events werden erstellt.&lt;br /&gt;
&lt;br /&gt;
Die meteorologischen Bedingungen verändern sich über den Tag permanent. Je nach gewählter Strahlungsdatenquelle (eine API oder DWD Device) erfolgt eine mehr oder weniger dynamische Anpassung der Prognose an die sich verändernde Umwelt.&lt;br /&gt;
&lt;br /&gt;
Aktuelle Prognosedaten sowie die reale PV Erzeugung der vergangenen Stunde können über die Readings&lt;br /&gt;
&lt;br /&gt;
 LastHourPVforecast&lt;br /&gt;
 LastHourPVreal&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2024-10-20 134050.png|right|thumb|400px|Übersicht solare Vorhersage]]&lt;br /&gt;
geloggt werden. &lt;br /&gt;
&lt;br /&gt;
Werden alle drei Werte in einem SVG kombiniert, kann die Entwicklung der Prognose über den Tag und die Beziehung von Prognose zu realer PV Erzeugnung pro Stunde visualisiert werden.&lt;br /&gt;
&lt;br /&gt;
Für das rechts abgebildete Beispiel des Plots aus einer Datenbank &amp;quot;LogDBShort&amp;quot; sieht das gplot-File wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Created by FHEM/98_SVG.pm, 2024-03-08 15:05:31&lt;br /&gt;
set terminal png transparent size &amp;lt;SIZE&amp;gt; crop&lt;br /&gt;
set output &#039;&amp;lt;OUT&amp;gt;.png&#039;&lt;br /&gt;
set xdata time&lt;br /&gt;
set timefmt &amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;&lt;br /&gt;
set xlabel &amp;quot; &amp;quot;&lt;br /&gt;
set title &#039;&amp;lt;TL&amp;gt;&#039;&lt;br /&gt;
set ytics &lt;br /&gt;
set y2tics &lt;br /&gt;
set grid&lt;br /&gt;
set ylabel &amp;quot;Wh&amp;quot;&lt;br /&gt;
set y2label &amp;quot;Wh&amp;quot;&lt;br /&gt;
set yrange [0:7500]&lt;br /&gt;
set y2range [0:7500]&lt;br /&gt;
&lt;br /&gt;
#LogDBShort SolCast:LastHourPVforecast:::&lt;br /&gt;
#LogDBShort SolCast:LastHourPVreal:::&lt;br /&gt;
#LogDBShort SolCast:AllPVforecastsToEvent:::&lt;br /&gt;
&lt;br /&gt;
plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;aktuelle PV Vorhersage&#039; ls l6fill lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;reale PV Erzeugung&#039; ls l2fill lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;initiale PV Vorhersage&#039; ls l4 lw 1 with lines&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; In manchen Fällen werden die Vorhersagewerte im SVG-Plot nicht angezeigt wenn FileLog verwendet wird. Ein Setzen des Attribute &amp;quot;fixedrange=3days +1&amp;quot; im SVG löst das Problem. Die Definition des SVG-Devices:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define SVG_LogDBShort_SolCast SVG LogDBShort:SVG_LogDBShort_SolCast:HISTORY&lt;br /&gt;
attr SVG_LogDBShort_SolCast fixedrange 3days +1&lt;br /&gt;
attr SVG_LogDBShort_SolCast room Energie-&amp;gt;SolarPrognose&lt;br /&gt;
attr SVG_LogDBShort_SolCast sortby 2&lt;br /&gt;
attr SVG_LogDBShort_SolCast title &amp;quot;Übersicht solare Vorhersage&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Technisch bedingt werden an einem Tag X kurz nach 00:00 Uhr die am Vortag für den Tag X erzeugten Events aktualisiert. Diese Daten erzeugen in der Datenbank einen zweiten Satz an Daten mit dem gleichen Timestamp was für die Anzeige unvorteilhaft ist. &lt;br /&gt;
Um nur die aktualisierten Events von &#039;&#039;&#039;AllPVforecastsToEvent&#039;&#039;&#039; in der Datenbank zu erhalten, bietet sich ein DbRep-Device an. Die nachfolgende Vorschlagsdefinition kann per at-Device jeden Tag kurz vor Mitternacht (z.B. 23:57:10) ausgeführt werden. Es werden immer die in der Vergangenheit geschriebenen (veralteten) Daten aus der Datenbank gelöscht, doppelte Datesätze vermieden und immer die aktuellste initiale Prognose gespeichert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Rep.Del.AllPVforecastsToEvent DbRep LogDBShort&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent alias Löschen Readings AllPVforecastsToEvent des Folgetages&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent comment ermöglicht dass die Readings AllPVforecastsToEvent am Folgetag wieder neu und aktuell geschrieben werden können&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent devStateIcon initialized:control_3dot_hor_s connected:10px-kreis-gelb .*disconnect:10px-kreis-rot .*done:10px-kreis-gruen&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent disable 0&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent event-on-update-reading state&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent icon edit_delete&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent room Datenbank-&amp;gt;Produktiv&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent showproctime 1&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent device TYPE=SolarForecast&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent timestamp_begin next_day_begin&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent timestamp_end next_day_end&lt;br /&gt;
attr Rep.Del.AllPVforecastsToEvent verbose 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das angegebene DbLog-Device &#039;&#039;&#039;LogDBShort&#039;&#039;&#039; ist natürlich anzupassen.&lt;br /&gt;
&lt;br /&gt;
Das at-Device zum Starten der Datenbankbereinigung sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define At.Del.AllPVforecastsToEvent at *23:58:10 set Rep.Del.AllPVforecastsToEvent delEntries&lt;br /&gt;
attr At.Del.AllPVforecastsToEvent alias Start Löschen Readings AllPVforecastsToEventn LogDBShort&lt;br /&gt;
attr At.Del.AllPVforecastsToEvent icon clock&lt;br /&gt;
attr At.Del.AllPVforecastsToEvent room Datenbank-&amp;gt;Produktiv&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Natürlich sind auch hier die eigenen Devicenamen entsprechend anzupassen.&lt;br /&gt;
&lt;br /&gt;
Ab SolarForecast Version 1.51.7 kann die Eventerzeugung für bestimmte SVG Plot-Typen optimiert werden. &amp;lt;br&amp;gt; &lt;br /&gt;
Die Aktivierung dieser Optimierungen ist in der Online Hilfe zum Attribut &#039;&#039;&#039;plantControl-&amp;gt;genPVforecastsToEvent&#039;&#039;&#039; beschrieben. Bei Nutzung des Attributes plantControl-&amp;gt;genPVforecastsToEvent ist ebenfalls das Attribut &#039;&#039;&#039;event-on-update-reading=AllPVforecastsToEvent&#039;&#039;&#039; zu setzen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Beispielkonfiguration 1 ===&lt;br /&gt;
Dies ist eine Konfiguration mit &lt;br /&gt;
&lt;br /&gt;
SummenDummy für 2 BatterieWR (Namen : SBS25 / SBS25_2)&lt;br /&gt;
&lt;br /&gt;
SummenDummy für 3 PV-Wechselrichter (Namen : SB25 / SB30 / SB40)&lt;br /&gt;
&lt;br /&gt;
            mit verschiedenen InverterStrings / ModulDirection / ModulTiltAngle, ModulPeakString&lt;br /&gt;
&lt;br /&gt;
und auch mit den notwendigen zugehörigen anderen Modul-Konfigs.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ACHTUNG:&#039;&#039;&#039; Zusätzlich enthalten ist bei dem Beispiel-Notify eine Sonderkonstellation für eine Brennstoffzelle &amp;quot;FCU&amp;quot; als weitere bzw. zusätzliche Stromerzeugungsquelle. Diese &amp;quot;FCU&amp;quot; wird dadurch mit in der Grafik mit deren Erzeugungsleistung Tag und Nacht in der Erzeugersumme (am Symbol = Sonne) berücksichtigt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;(ReadingsVal(&amp;quot;FCU&amp;quot;,&amp;quot;FCU-Strom-aktuelle-Leistung&amp;quot;,0)/1000)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== DWD ====&lt;br /&gt;
Bitte dabei diese DWD-Version aus dem [https://svn.fhem.de/trac/browser/trunk/fhem/contrib/DS_Starter Contrib] von DS_Starter dazu nutzen&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|55_DWD_OpenData.pm&lt;br /&gt;
|136.2 KB ​&lt;br /&gt;
|29260  &lt;br /&gt;
|DS_Starter&lt;br /&gt;
|55_DWD_OpenData: contrib 1.17.7&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define DWD DWD_OpenData&lt;br /&gt;
attr DWD downloadTimeout 120&lt;br /&gt;
attr DWD comment Im DWD Wetterdevice verwendet Solarforecast : \&lt;br /&gt;
TTT,Neff,RR1c,ww,SunUp,SunRise,SunSet \&lt;br /&gt;
zusätzlich ist jedoch auch noch Rad1h notwendig \&lt;br /&gt;
wenn dieses DWD-Device als Strahlungsdevice genutzt wird.\&lt;br /&gt;
Die Station 10418 (Lüdenscheid) überträgt aktuell am 09.03.2025 (noch) Rad1h.\&lt;br /&gt;
attr DWD downloadTimeout 120&lt;br /&gt;
attr DWD forecastDays 7&lt;br /&gt;
attr DWD forecastProperties SunUp, SunRise, SunSet, Rad1h, R101, RR1c, TTT, Tx, Tn, Tg, DD, FX1, RR6c, R600, RRhc, Rh00, ww, wwd, Neff&lt;br /&gt;
attr DWD forecastRefresh 1&lt;br /&gt;
attr DWD forecastResolution 1&lt;br /&gt;
attr DWD forecastStation 10418&lt;br /&gt;
attr DWD forecastWW2Text 1&lt;br /&gt;
attr DWD group Umwelt&lt;br /&gt;
attr DWD icon rc_WEB&lt;br /&gt;
attr DWD room 021_DWD&lt;br /&gt;
attr DWD stateFormat Tomorrow Tmax fc1_Tx °C on fc1_date at fc_description  -(state fc_time)&lt;br /&gt;
attr DWD verbose 2&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== InverterDummy ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod InverterDummy dummy&lt;br /&gt;
attr InverterDummy event-on-change-reading .*&lt;br /&gt;
attr InverterDummy group Energy Meter&lt;br /&gt;
attr InverterDummy icon measure_photovoltaic_inst@green&lt;br /&gt;
attr InverterDummy room 020_PV,Energie&lt;br /&gt;
attr InverterDummy stateFormat {sprintf(&amp;quot;current %9.3f kW    Today_PVforecast  %9.3f kWh      Today_PV %9.3f kWh      Total_PV %9.3f kWh&amp;quot;,\&lt;br /&gt;
ReadingsVal($name,&amp;quot;total_pac&amp;quot;,0)/1,\&lt;br /&gt;
ReadingsNum(&amp;quot;Forecast&amp;quot;,&amp;quot;Today_PVforecast&amp;quot;,0)/1000,\&lt;br /&gt;
ReadingsVal($name,&amp;quot;etoday&amp;quot;,0)/1,\&lt;br /&gt;
ReadingsVal($name,&amp;quot;etotal&amp;quot;,0)/1,)}&lt;br /&gt;
attr InverterDummy verbose 2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== SMA_Energymeter ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod SMA_Energymeter SMAEM&lt;br /&gt;
attr SMA_Energymeter DbLogExclude state&lt;br /&gt;
attr SMA_Energymeter diffAccept 50&lt;br /&gt;
attr SMA_Energymeter disable 0&lt;br /&gt;
attr SMA_Energymeter disableSernoInReading 1&lt;br /&gt;
attr SMA_Energymeter event-on-update-reading state,Saldo_Wirkleistung,Bezug_Wirkleistung,Einspeisung_Wirkleistung,Bezug_Wirkleistung_Zaehler,Einspeisung_Wirkleistung_Zaehler&lt;br /&gt;
attr SMA_Energymeter feedinPrice 0.08&lt;br /&gt;
attr SMA_Energymeter group Energy Meter&lt;br /&gt;
attr SMA_Energymeter icon measure_power@green&lt;br /&gt;
attr SMA_Energymeter interval 15&lt;br /&gt;
attr SMA_Energymeter powerCost 0.25&lt;br /&gt;
attr SMA_Energymeter room 015_Zaehler,020_PV,Energie&lt;br /&gt;
attr SMA_Energymeter serialNumber XXXXXXXXXX&lt;br /&gt;
attr SMA_Energymeter stateFormat state W (IN -) P1: L1_Saldo_Wirkleistung P2: L2_Saldo_Wirkleistung P3:L3_Saldo_Wirkleistung&lt;br /&gt;
attr SMA_Energymeter verbose 2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== BatteryDummy ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod BatteryDummy dummy&lt;br /&gt;
attr BatteryDummy DbLogExclude .*&lt;br /&gt;
attr BatteryDummy event-on-change-reading .*&lt;br /&gt;
attr BatteryDummy group Energy Meter&lt;br /&gt;
attr BatteryDummy icon batterie@green&lt;br /&gt;
attr BatteryDummy room 020_PV,Energie&lt;br /&gt;
attr BatteryDummy stateFormat {ReadingsVal(&amp;quot;$name&amp;quot;,&amp;quot;total_pac&amp;quot;, undef).&amp;quot; kW &amp;quot;.\&lt;br /&gt;
&amp;quot; - total &amp;quot;.ReadingsVal(&amp;quot;$name&amp;quot;,&amp;quot;bat_loadtotal&amp;quot;, undef).&amp;quot; kWh (-in)&amp;quot;.\&lt;br /&gt;
&amp;quot; - &amp;quot;.ReadingsVal(&amp;quot;$name&amp;quot;,&amp;quot;bat_unloadtotal&amp;quot;, undef).&amp;quot; kWh (out)&amp;quot;.\&lt;br /&gt;
&amp;quot; - charged &amp;quot;.ReadingsVal(&amp;quot;$name&amp;quot;,&amp;quot;chargestatus&amp;quot;, undef).&amp;quot; % &amp;quot;}&lt;br /&gt;
attr BatteryDummy userReadings total_pac, power_out, power_in, chargestatus, bat_rated_capacity, bat_loadtotal, bat_unloadtotal&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;quot;Berechnungs&amp;quot;-Notify der Werte für Batterie- und InverterDummy ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod N.PV.TotalConsumption.Dum.Energy notify SMA_Energymeter:Saldo_Wirkleistung:.* {\&lt;br /&gt;
 # Batterie-Bezug -Batterieentnahme\&lt;br /&gt;
fhem &amp;quot;setreading Dum.Energy BattLoadIn &amp;quot;.sprintf(&amp;quot;%.1f&amp;quot;,(ReadingsVal(&amp;quot;SBS25&amp;quot;,&amp;quot;power_out&amp;quot;,0)));;\&lt;br /&gt;
 # Batterie-Beladung Batterie mit Strom füllen\&lt;br /&gt;
fhem &amp;quot;setreading Dum.Energy BattLoadOut &amp;quot;.sprintf(&amp;quot;%.1f&amp;quot;,(ReadingsVal(&amp;quot;SBS25&amp;quot;,&amp;quot;power_in&amp;quot;,0)));;\&lt;br /&gt;
 # Batteriestatus\&lt;br /&gt;
fhem &amp;quot;setreading Dum.Energy BattStatusP &amp;quot;.sprintf(&amp;quot;%.1f&amp;quot;,(ReadingsVal(&amp;quot;SBS25&amp;quot;,&amp;quot;chargestatus&amp;quot;,0)));;\&lt;br /&gt;
 # Batterie-Bezug -Batterieentnahme_2\&lt;br /&gt;
fhem &amp;quot;setreading Dum.Energy BattLoadIn_2 &amp;quot;.sprintf(&amp;quot;%.1f&amp;quot;,(ReadingsVal(&amp;quot;SBS25_2&amp;quot;,&amp;quot;power_out&amp;quot;,0)));;\&lt;br /&gt;
 # Batterie-Beladung_2 Batterie mit Strom füllen\&lt;br /&gt;
fhem &amp;quot;setreading Dum.Energy BattLoadOut_2 &amp;quot;.sprintf(&amp;quot;%.1f&amp;quot;,(ReadingsVal(&amp;quot;SBS25_2&amp;quot;,&amp;quot;power_in&amp;quot;,0)));;\&lt;br /&gt;
 # Batteriestatus_2\&lt;br /&gt;
fhem &amp;quot;setreading Dum.Energy BattStatusP_2 &amp;quot;.sprintf(&amp;quot;%.1f&amp;quot;,(ReadingsVal(&amp;quot;SBS25_2&amp;quot;,&amp;quot;chargestatus&amp;quot;,0)));;\&lt;br /&gt;
 # Forecast Invertererzeugung InverterDummy \&lt;br /&gt;
fhem &amp;quot;setreading InverterDummy Today_PVforecast &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,(ReadingsNum(&amp;quot;Forecast&amp;quot;,&amp;quot;Today_PVforecast&amp;quot;,0)));;\&lt;br /&gt;
 # Invertererzeugung InverterDummy \&lt;br /&gt;
fhem &amp;quot;setreading InverterDummy etotal &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,(ReadingsNum(&amp;quot;SB25&amp;quot;,&amp;quot;etotal&amp;quot;,0))+(ReadingsNum(&amp;quot;SB30&amp;quot;,&amp;quot;etotal&amp;quot;,0))+(ReadingsNum(&amp;quot;SB40&amp;quot;,&amp;quot;etotal&amp;quot;,0)));;\&lt;br /&gt;
 # Invertererzeugung InverterDummy \&lt;br /&gt;
 #fhem &amp;quot;setreading InverterDummy total_pac &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,(ReadingsVal(&amp;quot;MB_USRW610_004&amp;quot;,&amp;quot;Power_Sum__W&amp;quot;,0)/1000)+(ReadingsNum(&amp;quot;SB25&amp;quot;,&amp;quot;total_pac&amp;quot;,0))+(ReadingsNum(&amp;quot;SB30&amp;quot;,&amp;quot;total_pac&amp;quot;,0))+(ReadingsNum(&amp;quot;SB40&amp;quot;,&amp;quot;total_pac&amp;quot;,0)));;\&lt;br /&gt;
fhem &amp;quot;setreading InverterDummy total_pac &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,(ReadingsNum(&amp;quot;SB25&amp;quot;,&amp;quot;total_pac&amp;quot;,0))+(ReadingsNum(&amp;quot;SB30&amp;quot;,&amp;quot;total_pac&amp;quot;,0))+(ReadingsNum(&amp;quot;SB40&amp;quot;,&amp;quot;total_pac&amp;quot;,0)));;\&lt;br /&gt;
#fhem &amp;quot;setreading InverterDummy total_pac &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,(ReadingsVal(&amp;quot;FCU&amp;quot;,&amp;quot;FCU-Strom-aktuelle-Leistung&amp;quot;,0)/1000)+(ReadingsNum(&amp;quot;SB25&amp;quot;,&amp;quot;total_pac&amp;quot;,0))+(ReadingsNum(&amp;quot;SB30&amp;quot;,&amp;quot;total_pac&amp;quot;,0))+(ReadingsNum(&amp;quot;SB40&amp;quot;,&amp;quot;total_pac&amp;quot;,0)));;\&lt;br /&gt;
 # Invertererzeugung InverterDummy \&lt;br /&gt;
my $wert1234 = &amp;quot;0&amp;quot; ;;\&lt;br /&gt;
$wert1234 = sprintf(&amp;quot;%.3f&amp;quot;,(ReadingsNum(&amp;quot;SB25&amp;quot;,&amp;quot;etoday&amp;quot;,0))+(ReadingsNum(&amp;quot;SB30&amp;quot;,&amp;quot;etoday&amp;quot;,0))+(ReadingsNum(&amp;quot;SB40&amp;quot;,&amp;quot;etoday&amp;quot;,0)));; \&lt;br /&gt;
fhem (&amp;quot;setreading InverterDummy etoday &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,$wert1234));;\&lt;br /&gt;
 # Batterie-Bezug -Batterieentnahme InverterDummy\&lt;br /&gt;
fhem &amp;quot;setreading BatteryDummy power_out &amp;quot;.sprintf(&amp;quot;%.0f&amp;quot;,(ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;power_out&amp;quot;,0))+(ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;power_out&amp;quot;,0)));;\&lt;br /&gt;
 # Batterie-Beladung InverterDummyBatterie mit Strom füllen\&lt;br /&gt;
fhem &amp;quot;setreading BatteryDummy power_in &amp;quot;.sprintf(&amp;quot;%.0f&amp;quot;,(ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;power_in&amp;quot;,0))+(ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;power_in&amp;quot;,0)));;\&lt;br /&gt;
 # Batterie-Bezug -bat_loadtotal Batterieentnahme InverterDummy\&lt;br /&gt;
fhem &amp;quot;setreading BatteryDummy bat_unloadtotal &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,(ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;bat_unloadtotal&amp;quot;,0))+(ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;bat_unloadtotal&amp;quot;,0)));;\&lt;br /&gt;
 # Batterie-Beladung bat_loadtotal InverterDummyBatterie mit Strom füllen\&lt;br /&gt;
fhem &amp;quot;setreading BatteryDummy bat_loadtotal &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,(ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;bat_loadtotal&amp;quot;,0))+(ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;bat_loadtotal&amp;quot;,0)));;\&lt;br /&gt;
 # Batteriestatus InverterDummy\&lt;br /&gt;
my $wert5 = sprintf(&amp;quot;%.2f&amp;quot;,(((ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;chargestatus&amp;quot;,0))/2) + ((ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;chargestatus&amp;quot;,0))/2)));; \&lt;br /&gt;
fhem (&amp;quot;setreading BatteryDummy chargestatus &amp;quot;.sprintf(&amp;quot;%.2f&amp;quot;,$wert5));;\&lt;br /&gt;
 # Batterie-total_pac  InverterDummy\&lt;br /&gt;
my $wert6 = sprintf(&amp;quot;%.3f&amp;quot;,((ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;total_pac&amp;quot;,0))+(ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;total_pac&amp;quot;,0))));; \&lt;br /&gt;
fhem (&amp;quot;setreading BatteryDummy total_pac &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,$wert6));;\&lt;br /&gt;
 # Batterie-total_pac  InverterDummy\&lt;br /&gt;
my $wert7 = sprintf(&amp;quot;%.3f&amp;quot;,((ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;bat_rated_capacity&amp;quot;,0))+(ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;bat_rated_capacity&amp;quot;,0))));; \&lt;br /&gt;
fhem (&amp;quot;setreading BatteryDummy bat_rated_capacity &amp;quot;.sprintf(&amp;quot;%.3f&amp;quot;,$wert7));;\&lt;br /&gt;
 # möglicher Aufruf einer Batterie-Bebladung-Routine....SMABatteryChargewithTibber();;\&lt;br /&gt;
}&lt;br /&gt;
attr N.PV.TotalConsumption.Dum.Energy DbLogExclude .*&lt;br /&gt;
attr N.PV.TotalConsumption.Dum.Energy room Energie&lt;br /&gt;
attr N.PV.TotalConsumption.Dum.Energy verbose 2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== SolarForecast ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod Forecast SolarForecast&lt;br /&gt;
attr Forecast DbLogExclude .*&lt;br /&gt;
attr Forecast DbLogInclude Current_BatCharge999&lt;br /&gt;
attr Forecast affectBatteryPreferredCharge 30&lt;br /&gt;
attr Forecast affectConsForecastLastDays 31&lt;br /&gt;
attr Forecast affect70percentRule 0&lt;br /&gt;
attr Forecast comment &amp;quot;wget -qO ./FHEM/76_SolarForecast.pm https://svn.fhem.de/fhem/trunk/fhem/contrib/DS_Starter/76_SolarForecast.pm&amp;quot;\&lt;br /&gt;
&amp;quot;wget -qO ./FHEM/55_DWD_OpenData.pm https://svn.fhem.de/fhem/trunk/fhem/contrib/DS_Starter/55_DWD_OpenData.pm&amp;quot;\&lt;br /&gt;
&amp;quot;13.01.2025 mit der Version 76_SolarForecast.pm:v1.43.2-s29518/2025-01-12&amp;quot;&lt;br /&gt;
attr Forecast graphicHeaderOwnspec PV&amp;amp;nbsp;;Heute&amp;amp;nbsp;;real:Today_PVreal Verbrauch&amp;amp;nbsp;;bis&amp;amp;nbsp;;Sonnenaufgang&amp;amp;nbsp;;:special_conForecastTillNextSunrise PV&amp;amp;nbsp;;Morgen&amp;amp;nbsp;;erwartet:Tomorrow_PVforecast PV&amp;amp;nbsp;;Uebermorgen&amp;amp;nbsp;;erwartet:special_dayAfterTomorrowPVforecast  Batt.-Ladeanforderung&amp;amp;nbsp;;:Battery_ChargeRequest FCU-Erzeugung&amp;amp;nbsp;;:Current_PP01\&lt;br /&gt;
attr Forecast consumer01 FBDECT_fbahahttp_11657_0127183 icon=scene_washing_machine@orange type=washingmachine power=10 swstate:state notbefore=09 notafter=20 pcurr=power:W:3 etotal=energy:Wh interruptable=1 auto=solarforecast_auto surpmeth=default&lt;br /&gt;
attr Forecast consumer02 FBDECT_fbahahttp_E8_DF_70_07_3E_57 icon=light_floor_lamp@orange type=other power=15 swstate:state pcurr=power:W:10 etotal=energy:Wh interruptable=0 auto=solarforecast_auto surpmeth=default&lt;br /&gt;
attr Forecast consumer03 FBDECT_fbahahttp_E8_DF_70_07_42_0B icon=raspberrypi@orange type=other power=8 swstate:state pcurr=power:W:1 etotal=energy:Wh interruptable=0 auto=solarforecast_auto surpmeth=default&lt;br /&gt;
attr Forecast consumer04 FBDECT_fbahahttp_11657_0067275 icon=springbrunnen_icon@orange type=other power=50 swstate:state pcurr=power:W:10 etotal=energy:Wh interruptable=0 auto=solarforecast_auto surpmeth=default&lt;br /&gt;
attr Forecast consumer05 FBDECT_fbahahttp_34_31_C4_D4_31_37 icon=sani_domestic_waterworks@orange type=other power=10 swstate:state pcurr=power:W:3 etotal=energy:Wh auto=solarforecast_auto surpmeth=default&lt;br /&gt;
attr Forecast consumer06 tuya_local_bf5037060f450bdbd4rl0q icon=scene_clothes_dryer@orange type=dryer power=10 swstate:state pcurr=cur_power:W:3 etotal=energy:Wh auto=solarforecast_auto surpmeth=default&lt;br /&gt;
attr Forecast consumer07 tuya_local_bfac44fb487476efd1vhdu icon=weather_sunset@orange type=noSchedule power=5 swstate:state pcurr=cur_power:W:3 etotal=energy:Wh auto=solarforecast_auto surpmeth=default&lt;br /&gt;
attr Forecast consumerControl adviceIcon=light_light_dim_100@gold&lt;br /&gt;
attr Forecast consumerControl showLegend=icon_top&lt;br /&gt;
attr Forecast consumerControl detailLink=1&lt;br /&gt;
attr Forecast ctrlAIdataStorageDuration 1825&lt;br /&gt;
attr Forecast ctrlAIshiftTrainStart 2&lt;br /&gt;
attr Forecast ctrlBatSocManagement01 lowSoc=10 upSoC=30 maxSoC=99 careCycle=20&lt;br /&gt;
attr Forecast ctrlBatSocManagement02 lowSoc=10 upSoC=30 maxSoC=99 careCycle=20&lt;br /&gt;
attr Forecast ctrlDebug none&lt;br /&gt;
attr Forecast plantControl genPVdeviation=continuously&lt;br /&gt;
attr Forecast plantControl cycleInterval=15&lt;br /&gt;
attr Forecast ctrlNextDayForecastReadings 01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24&lt;br /&gt;
attr Forecast ctrlNextHoursSoCForecastReadings 00,01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16,17,18,19,20,21,22,23&lt;br /&gt;
attr Forecast ctrlSpecialReadings BatPowerIn_Sum,BatPowerOut_Sum,SunHours_Remain,SunMinutes_Remain,allStringsFullfilled,conForecastTillNextSunrise,currentAPIinterval,currentRunMtsConsumer_01,currentRunMtsConsumer_02,currentRunMtsConsumer_03,currentRunMtsConsumer_04,currentRunMtsConsumer_05,currentRunMtsConsumer_06,currentRunMtsConsumer_07,dayAfterTomorrowPVforecast,daysUntilBatteryCare_01,daysUntilBatteryCare_02,lastretrieval_time,lastretrieval_timestamp,response_message,runTimeAvgDayConsumer_01,runTimeAvgDayConsumer_02,runTimeAvgDayConsumer_03,runTimeAvgDayConsumer_04,runTimeAvgDayConsumer_05,runTimeAvgDayConsumer_06,runTimeAvgDayConsumer_07,runTimeCentralTask,runTimeLastAPIAnswer,runTimeLastAPIProc,runTimeTrainAI,todayBatIn_01,todayBatIn_02,todayBatOut_01,todayBatOut_02,todayConForecastTillSunset,todayConsumptionForecast,todayDoneAPIcalls,todayDoneAPIrequests,todayGridConsumption,todayGridFeedIn,todayMaxAPIcalls,todayRemainingAPIcalls,todayRemainingAPIrequests&lt;br /&gt;
attr Forecast disable 0&lt;br /&gt;
attr Forecast event-min-interval .*:1800&lt;br /&gt;
attr Forecast event-on-change-reading .*&lt;br /&gt;
attr Forecast flowGraphicControl animate=1 consumerdist=80 h2consumerdist=50 shiftx=0  shifty=0 showconsumer=1 showconsumerdummy=1 showconsumerpower=1 showconsumerremaintime=0 size=400 strokewidth=12&lt;br /&gt;
attr Forecast graphicBeam1Color 3C14FF&lt;br /&gt;
attr Forecast graphicBeam1Content pvReal&lt;br /&gt;
attr Forecast graphicBeam2Color 19FF29&lt;br /&gt;
attr Forecast graphicBeam2Content pvForecast&lt;br /&gt;
attr Forecast graphicBeam3Color D60924&lt;br /&gt;
attr Forecast graphicBeam3Content batsocforecast_02&lt;br /&gt;
attr Forecast graphicBeam3FontColor FFFF0D&lt;br /&gt;
attr Forecast graphicBeam4Color FFFF1F&lt;br /&gt;
attr Forecast graphicBeam4Content batsocforecast_01&lt;br /&gt;
attr Forecast graphicBeam4FontColor 000000&lt;br /&gt;
attr Forecast graphicBeamHeightLevel1 200&lt;br /&gt;
attr Forecast graphicBeamHeightLevel2 200&lt;br /&gt;
attr Forecast graphicHeaderDetail all&lt;br /&gt;
attr Forecast graphicHeaderOwnspec #PV\&lt;br /&gt;
PV&amp;amp;nbsp;;Heute&amp;amp;nbsp;;real:Today_PVreal\&lt;br /&gt;
PV&amp;amp;nbsp;;Morgen&amp;amp;nbsp;;erwartet:Tomorrow_PVforecast\&lt;br /&gt;
PV&amp;amp;nbsp;;Uebermorgen&amp;amp;nbsp;;erwartet:special_dayAfterTomorrowPVforecast\&lt;br /&gt;
:\&lt;br /&gt;
#\&lt;br /&gt;
AutarkyRate:Current_AutarkyRate\&lt;br /&gt;
Überschuss:Current_Surplus\&lt;br /&gt;
aktueller&amp;amp;nbsp;;Netzbezug:Current_GridConsumption\&lt;br /&gt;
:\&lt;br /&gt;
#Verbrauch\&lt;br /&gt;
bis&amp;amp;nbsp;;Sonnenuntergang:special_todayConForecastTillSunset\&lt;br /&gt;
bis&amp;amp;nbsp;;Sonnenaufgang&amp;amp;nbsp;;:special_conForecastTillNextSunrise\&lt;br /&gt;
:\&lt;br /&gt;
:\&lt;br /&gt;
#Batterie01\&lt;br /&gt;
Batt.-Ladeanforderung&amp;amp;nbsp;;:Battery_ChargeRequest_01\&lt;br /&gt;
Batt.-Ladung&amp;amp;nbsp;;empfohlen:Battery_ChargeUnrestricted_01\&lt;br /&gt;
Ladung&amp;amp;nbsp;;heute:special_todayBatIn_01\&lt;br /&gt;
Entladung&amp;amp;nbsp;;heute:special_todayBatOut_01\&lt;br /&gt;
#Batterie01_tmp\&lt;br /&gt;
Current_PowerBatIn_01:Current_PowerBatIn_01\&lt;br /&gt;
Current_PowerBatOut_01:Current_PowerBatOut_01\&lt;br /&gt;
:\&lt;br /&gt;
:\&lt;br /&gt;
#Batterie02\&lt;br /&gt;
Batt.-Ladeanforderung&amp;amp;nbsp;;:Battery_ChargeRequest_02\&lt;br /&gt;
Batt.-Ladung&amp;amp;nbsp;;empfohlen:Battery_ChargeUnrestricted_02\&lt;br /&gt;
Ladung&amp;amp;nbsp;;heute:special_todayBatIn_02\&lt;br /&gt;
Entladung&amp;amp;nbsp;;heute:special_todayBatOut_02\&lt;br /&gt;
#Batterie02_tmp\&lt;br /&gt;
Current_PowerBatIn_02:Current_PowerBatIn_02\&lt;br /&gt;
Current_PowerBatOut_02:Current_PowerBatOut_02\&lt;br /&gt;
:\&lt;br /&gt;
:\&lt;br /&gt;
#Settings\&lt;br /&gt;
Autokorrektur:pvCorrectionFactor_Auto \&lt;br /&gt;
Wetter:graphicShowWeather\&lt;br /&gt;
History:graphicHistoryHour\&lt;br /&gt;
ShowNight:graphicShowNight\&lt;br /&gt;
Debug:ctrlDebug\&lt;br /&gt;
FCU-Modus:FCU-Betriebsmodus@FCU&lt;br /&gt;
attr Forecast graphicHistoryHour 4&lt;br /&gt;
attr Forecast graphicHourCount 24&lt;br /&gt;
attr Forecast graphicLayoutType double&lt;br /&gt;
attr Forecast graphicShowDiff top&lt;br /&gt;
attr Forecast graphicShowNight 1&lt;br /&gt;
attr Forecast graphicShowWeather 1&lt;br /&gt;
attr Forecast group Energy Meter&lt;br /&gt;
attr Forecast room 020_PV,Energie&lt;br /&gt;
attr Forecast setupBatteryDev01 SBS25 pin=-pout:kW pout=total_pac:kW intotal=bat_loadtotal:kWh outtotal=bat_unloadtotal:kWh charge=chargestatus cap=9800 show=2&lt;br /&gt;
attr Forecast setupBatteryDev02 SBS25_2 pin=-pout:kW pout=total_pac:kW intotal=bat_loadtotal:kWh outtotal=bat_unloadtotal:kWh charge=chargestatus cap=9800 show=2&lt;br /&gt;
attr Forecast setupInverterDev01 SB25 pv=total_pac:kW etotal=etotal:kWh capacity=2500 strings=GarageSE limit=70&lt;br /&gt;
attr Forecast setupInverterDev02 SB30 pv=total_pac:kW etotal=etotal:kWh capacity=3000 strings=GarageNW,HausNW limit=70&lt;br /&gt;
attr Forecast setupInverterDev03 SB40 pv=total_pac:kW etotal=etotal:kWh capacity=4000 strings=HausSE1,HausSE2,HausSW limit=70&lt;br /&gt;
attr Forecast setupInverterStrings GarageSE,GarageNW,HausNW,HausSW,HausSE1,HausSE2&lt;br /&gt;
attr Forecast setupMeterDev SMA_Energymeter gcon=Bezug_Wirkleistung:W contotal=Bezug_Wirkleistung_Zaehler:kWh gfeedin=Einspeisung_Wirkleistung:W feedtotal=Einspeisung_Wirkleistung_Zaehler:kWh conprice=0.25:€ feedprice=0.08123:€&lt;br /&gt;
attr Forecast setupOtherProducer01 icon=Heizung_FCU_green@red MB_USRW610_004 pcurr=Power_L1__W:W etotal=Energy_L1_import__kWh:kWh&lt;br /&gt;
attr Forecast setupRadiationAPI DWD&lt;br /&gt;
attr Forecast setupStringPeak GarageSE=2.75 GarageNW=3.200 HausNW=2.230 HausSW=2.230 HausSE1=2.200 HausSE2=2.200&lt;br /&gt;
attr Forecast setupWeatherDev1 DWD&lt;br /&gt;
attr Forecast stateFormat Current_PV&lt;br /&gt;
attr Forecast userReadings Current_BatCharge999 {((ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;chargestatus&amp;quot;,0) * 10 * ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;bat_rated_capacity&amp;quot;,0))  + (ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;chargestatus&amp;quot;,0) * 10 * ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;bat_rated_capacity&amp;quot;,0))) / ( (ReadingsNum(&amp;quot;SBS25&amp;quot;,&amp;quot;bat_rated_capacity&amp;quot;,0) * 1000)  + (ReadingsNum(&amp;quot;SBS25_2&amp;quot;,&amp;quot;bat_rated_capacity&amp;quot;,0)*1000))*100}&lt;br /&gt;
attr Forecast verbose 2&lt;br /&gt;
&lt;br /&gt;
setstate Forecast 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:55 .FCU_FCU-Betriebsmodus 2&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:55 .Forecast_ctrlDebug none&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:55 .Forecast_graphicHistoryHour 4&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:55 .Forecast_graphicShowNight 1&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:55 .Forecast_graphicShowWeather 1&lt;br /&gt;
setstate Forecast 2025-01-13 08:24:25 .associatedWith SMA_Energymeter FBDECT_fbahahttp_11657_0127183 FBDECT_fbahahttp_E8_DF_70_07_3E_57 FBDECT_fbahahttp_E8_DF_70_07_42_0B FBDECT_fbahahttp_11657_0067275 FBDECT_fbahahttp_34_31_C4_D4_31_37 tuya_local_bf5037060f450bdbd4rl0q tuya_local_bfac44fb487476efd1vhdu SBS25 SBS25_2 SB25 SB30 SB40 DWD MB_USRW610_004&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 .lastupdateForecastValues 1736790834&lt;br /&gt;
setstate Forecast 2025-01-13 01:00:05 .pvCorrectionFactor_01_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 01:00:05 .pvCorrectionFactor_01_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 02:00:04 .pvCorrectionFactor_02_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 02:00:04 .pvCorrectionFactor_02_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 03:00:04 .pvCorrectionFactor_03_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 03:00:04 .pvCorrectionFactor_03_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 04:00:05 .pvCorrectionFactor_04_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 04:00:05 .pvCorrectionFactor_04_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 05:00:05 .pvCorrectionFactor_05_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 05:00:05 .pvCorrectionFactor_05_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 06:00:06 .pvCorrectionFactor_06_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 06:00:06 .pvCorrectionFactor_06_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 07:00:04 .pvCorrectionFactor_07_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 07:00:04 .pvCorrectionFactor_07_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 08:00:03 .pvCorrectionFactor_08_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 08:00:03 .pvCorrectionFactor_08_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 09:00:04 .pvCorrectionFactor_09_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 09:00:04 .pvCorrectionFactor_09_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 10:00:05 .pvCorrectionFactor_10_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 10:00:05 .pvCorrectionFactor_10_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 11:00:05 .pvCorrectionFactor_11_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 11:00:05 .pvCorrectionFactor_11_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 12:00:02 .pvCorrectionFactor_12_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 12:00:02 .pvCorrectionFactor_12_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 13:00:02 .pvCorrectionFactor_13_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 13:00:02 .pvCorrectionFactor_13_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 14:00:03 .pvCorrectionFactor_14_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 14:00:03 .pvCorrectionFactor_14_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 15:00:05 .pvCorrectionFactor_15_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 15:00:05 .pvCorrectionFactor_15_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 16:00:06 .pvCorrectionFactor_16_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 16:00:06 .pvCorrectionFactor_16_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 17:00:05 .pvCorrectionFactor_17_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 17:00:05 .pvCorrectionFactor_17_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 18:00:04 .pvCorrectionFactor_18_apipercentil done&lt;br /&gt;
setstate Forecast 2025-01-13 18:00:04 .pvCorrectionFactor_18_cloudcover done&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 .pvCorrectionFactor_Auto_Soll on_complex_ai&lt;br /&gt;
setstate Forecast 2025-01-13 01:00:05 .signaldone_01 done&lt;br /&gt;
setstate Forecast 2025-01-13 02:00:04 .signaldone_02 done&lt;br /&gt;
setstate Forecast 2025-01-13 03:00:04 .signaldone_03 done&lt;br /&gt;
setstate Forecast 2025-01-13 04:00:05 .signaldone_04 done&lt;br /&gt;
setstate Forecast 2025-01-13 05:00:05 .signaldone_05 done&lt;br /&gt;
setstate Forecast 2025-01-13 06:00:06 .signaldone_06 done&lt;br /&gt;
setstate Forecast 2025-01-13 07:00:04 .signaldone_07 done&lt;br /&gt;
setstate Forecast 2025-01-13 08:00:03 .signaldone_08 done&lt;br /&gt;
setstate Forecast 2025-01-13 09:00:04 .signaldone_09 done&lt;br /&gt;
setstate Forecast 2025-01-13 10:00:05 .signaldone_10 done&lt;br /&gt;
setstate Forecast 2025-01-13 11:00:05 .signaldone_11 done&lt;br /&gt;
setstate Forecast 2025-01-13 12:00:02 .signaldone_12 done&lt;br /&gt;
setstate Forecast 2025-01-13 13:00:02 .signaldone_13 done&lt;br /&gt;
setstate Forecast 2025-01-13 14:00:03 .signaldone_14 done&lt;br /&gt;
setstate Forecast 2025-01-13 15:00:05 .signaldone_15 done&lt;br /&gt;
setstate Forecast 2025-01-13 16:00:06 .signaldone_16 done&lt;br /&gt;
setstate Forecast 2025-01-13 17:00:05 .signaldone_17 done&lt;br /&gt;
setstate Forecast 2025-01-13 18:00:04 .signaldone_18 done&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_ChargeUnrestricted_01 1&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_ChargeUnrestricted_02 1&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_ChargeRequest_01 1&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_ChargeRequest_02 1&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour00_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour00_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour01_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour01_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour02_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour02_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour03_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour03_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour04_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour04_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour05_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour05_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour06_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour06_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour07_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour07_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour08_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour08_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour09_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour09_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour10_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour10_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour11_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour11_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour12_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour12_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour13_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour13_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour14_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour14_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour15_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour15_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour16_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour16_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour17_SoCforecast_01 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour17_SoCforecast_02 10.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour18_SoCforecast_01 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour18_SoCforecast_02 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour19_SoCforecast_01 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour19_SoCforecast_02 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour20_SoCforecast_01 42.6 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour20_SoCforecast_02 42.6 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour21_SoCforecast_01 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour21_SoCforecast_02 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour22_SoCforecast_01 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour22_SoCforecast_02 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour23_SoCforecast_01 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_NextHour23_SoCforecast_02 40.0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_OptimumTargetSoC_01 40 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Battery_OptimumTargetSoC_02 40 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_AutarkyRate 83 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:55 Current_BatCharge999 10&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_BatCharge_01 12 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_BatCharge_02 8 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_Consumption 626 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_GridConsumption 2 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_GridFeedIn 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_PP_01 746.0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_PV 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_PowerBatIn_01 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_PowerBatIn_02 132 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_PowerBatOut_01 10 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_PowerBatOut_02 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_SelfConsumption 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_SelfConsumptionRate 0 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Current_Surplus 120 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:00:00 LastHourGridconsumptionReal 5 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:00:00 LastHourPVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:00:00 LastHourPVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 NextHours_Sum01_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 NextHours_Sum02_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 NextHours_Sum03_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 NextHours_Sum04_ConsumptionForecast 2944 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 NextHours_Sum04_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 RestOfDayConsumptionForecast 3609 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 RestOfDayPVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 Today_Hour01_BatIn_01 272 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 Today_Hour01_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 Today_Hour01_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 Today_Hour01_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 Today_Hour01_GridConsumption 1 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 Today_Hour01_GridFeedIn 1 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 Today_Hour01_PPreal_01 748 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 Today_Hour01_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 Today_Hour02_BatIn_01 266 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 Today_Hour02_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 Today_Hour02_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 Today_Hour02_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 Today_Hour02_GridConsumption 1 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 Today_Hour02_GridFeedIn 1 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 Today_Hour02_PPreal_01 787 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 Today_Hour02_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 Today_Hour03_BatIn_01 271 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 Today_Hour03_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 Today_Hour03_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 Today_Hour03_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 Today_Hour03_GridConsumption 2 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 Today_Hour03_GridFeedIn 2 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 Today_Hour03_PPreal_01 750 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 Today_Hour03_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 Today_Hour04_BatIn_01 266 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 Today_Hour04_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 Today_Hour04_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 Today_Hour04_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 Today_Hour04_GridConsumption 1 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 Today_Hour04_GridFeedIn 1 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 Today_Hour04_PPreal_01 745 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 Today_Hour04_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 Today_Hour05_BatIn_01 267 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 Today_Hour05_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 Today_Hour05_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 Today_Hour05_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 Today_Hour05_GridConsumption 2 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 Today_Hour05_GridFeedIn 2 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 Today_Hour05_PPreal_01 758 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 Today_Hour05_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 Today_Hour06_BatIn_01 255 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 Today_Hour06_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 Today_Hour06_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 Today_Hour06_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 Today_Hour06_GridConsumption 1 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 Today_Hour06_GridFeedIn 1 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 Today_Hour06_PPreal_01 752 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 Today_Hour06_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 Today_Hour07_BatIn_01 226 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 Today_Hour07_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 Today_Hour07_BatOut_01 50 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 Today_Hour07_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 Today_Hour07_GridConsumption 2 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 Today_Hour07_GridFeedIn 2 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 Today_Hour07_PPreal_01 681 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 Today_Hour07_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 Today_Hour08_BatIn_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 Today_Hour08_BatIn_02 233 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 Today_Hour08_BatOut_01 845 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 Today_Hour08_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 Today_Hour08_GridConsumption 4 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 Today_Hour08_GridFeedIn 4 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 Today_Hour08_PPreal_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 Today_Hour08_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_BatIn_01 37 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_BatIn_02 90 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_BatOut_01 351 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_BatOut_02 188 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_GridConsumption 447 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_GridFeedIn 4 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_PPreal_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_PVforecast 127 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 Today_Hour09_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_BatIn_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_GridConsumption 1650 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_GridFeedIn 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_PPreal_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_PVforecast 478 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 Today_Hour10_PVreal 26 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_BatIn_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_GridConsumption 513 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_GridFeedIn 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_PPreal_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_PVforecast 868 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 Today_Hour11_PVreal 97 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_BatIn_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_BatIn_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_GridConsumption 576 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_GridFeedIn 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_PPreal_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_PVforecast 1157 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 Today_Hour12_PVreal 320 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_BatIn_01 8 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_BatIn_02 4 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_GridConsumption 1272 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_GridFeedIn 7 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_PPreal_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_PVforecast 1238 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 Today_Hour13_PVreal 945 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_BatIn_01 410 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_BatIn_02 53 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_BatOut_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_BatOut_02 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_GridConsumption 955 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_GridFeedIn 31 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_PPreal_01 562 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_PVforecast 1168 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 Today_Hour14_PVreal 1410 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_BatIn_01 703 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_BatIn_02 500 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_BatOut_01 22 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_BatOut_02 18 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_GridConsumption 32 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_GridFeedIn 71 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_PPreal_01 777 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_PVforecast 1630 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 Today_Hour15_PVreal 1416 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_BatIn_01 296 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_BatIn_02 70 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_BatOut_01 74 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_BatOut_02 258 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_GridConsumption 13 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_GridFeedIn 17 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_PPreal_01 732 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_PVforecast 880 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 Today_Hour16_PVreal 1051 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_BatIn_01 47 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_BatIn_02 126 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_BatOut_01 102 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_BatOut_02 75 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_GridConsumption 15 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_GridFeedIn 23 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_PPreal_01 771 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_PVforecast 117 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 Today_Hour17_PVreal 86 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 Today_Hour18_BatIn_01 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 Today_Hour18_BatIn_02 410 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 Today_Hour18_BatOut_01 758 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 Today_Hour18_BatOut_02 67 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 Today_Hour18_GridConsumption 5 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 Today_Hour18_GridFeedIn 5 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 Today_Hour18_PPreal_01 723 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 Today_Hour18_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_Hour19_BatIn_01 349 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_Hour19_BatIn_02 124 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_Hour19_BatOut_01 73 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_Hour19_BatOut_02 450 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_Hour19_GridConsumption 8 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_Hour19_GridFeedIn 6 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_Hour19_PPreal_01 671 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_Hour19_PVreal 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_MaxPVforecast 1630 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_MaxPVforecastTime 2025-01-13 14:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_PVdeviation 30.17 %&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_PVforecast 7663 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_PVreal 5351 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_SunRise 08:28&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Today_SunSet 16:46&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_ConsumptionForecast 17043 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour01_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour02_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour03_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour04_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour05_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour06_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour07_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour08_PVforecast 10 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour09_PVforecast 161 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour10_PVforecast 275 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour11_PVforecast 532 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour12_PVforecast 730 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour13_PVforecast 1252 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour14_PVforecast 818 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour15_PVforecast 1465 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour16_PVforecast 303 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour17_PVforecast 20 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour18_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour19_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour20_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour21_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour22_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour23_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_Hour24_PVforecast 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_PVforecast 5566 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_SunRise 08:27&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 Tomorrow_SunSet 16:47&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer01 name=&#039;_Waschmaschine&#039; state=&#039;off&#039; mode=&#039;can&#039; planningstate=&#039;planned&#039;&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer01_currentPower 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer01_planned_start 14.01.2025 09:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer01_planned_stop 14.01.2025 11:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer02 name=&#039;_Esszimmer&#039; state=&#039;on&#039; mode=&#039;can&#039; planningstate=&#039;planned&#039;&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer02_currentPower 15.3 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer02_planned_start 14.01.2025 08:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer02_planned_stop 14.01.2025 09:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer03 name=&#039;_FCU&#039; state=&#039;on&#039; mode=&#039;can&#039; planningstate=&#039;planned&#039;&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer03_currentPower 7.15 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer03_planned_start 14.01.2025 07:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer03_planned_stop 14.01.2025 08:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer04 name=&#039;_Brunnen&#039; state=&#039;off&#039; mode=&#039;can&#039; planningstate=&#039;planned&#039;&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer04_currentPower 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer04_planned_start 14.01.2025 08:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer04_planned_stop 14.01.2025 09:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer05 name=&#039;_Zisterne&#039; state=&#039;off&#039; mode=&#039;can&#039; planningstate=&#039;planned&#039;&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer05_currentPower 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer05_planned_start 14.01.2025 07:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer05_planned_stop 14.01.2025 08:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer06 name=&#039;SW Trockner&#039; state=&#039;unknown&#039; mode=&#039;can&#039; planningstate=&#039;planned&#039;&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer06_currentPower 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer06_planned_start 14.01.2025 07:00:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer06_planned_stop 14.01.2025 08:30:00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer07 name=&#039;SW Urlaubslicht-Wohnzimmer&#039; state=&#039;on&#039; mode=&#039;can&#039; planningstate=&#039;noSchedule&#039; info=&#039;von extern umgeschaltet&#039;&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 consumer07_currentPower 0 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 nextCycletime 18:54:09&lt;br /&gt;
setstate Forecast 2025-01-13 10:00:05 pvCorrectionFactor_10 0.53 (automatic - old factor: 1.00, Sun Alt range: 5, Cloud range: 75, Days in range: 1)&lt;br /&gt;
setstate Forecast 2025-01-13 11:00:05 pvCorrectionFactor_11 0.56 (automatic - old factor: 1.00, Sun Alt range: 10, Cloud range: 70, Days in range: 1)&lt;br /&gt;
setstate Forecast 2025-01-13 12:00:02 pvCorrectionFactor_12 0.64 (automatic - old factor: 1.00, Sun Alt range: 15, Cloud range: 60, Days in range: 1)&lt;br /&gt;
setstate Forecast 2025-01-13 13:00:02 pvCorrectionFactor_13 0.88 (automatic - old factor: 1.00, Sun Alt range: 15, Cloud range: 60, Days in range: 1)&lt;br /&gt;
setstate Forecast 2025-01-13 14:00:03 pvCorrectionFactor_14 1.10 (automatic - old factor: 1.00, Sun Alt range: 15, Cloud range: 50, Days in range: 1)&lt;br /&gt;
setstate Forecast 2025-01-13 15:00:05 pvCorrectionFactor_15 1.01 (automatic - old factor: 1.06, Sun Alt range: 15, Cloud range: 45, Days in range: 2)&lt;br /&gt;
setstate Forecast 2025-01-13 16:00:06 pvCorrectionFactor_16 1.09 (automatic - old factor: 1.00, Sun Alt range: 10, Cloud range: 45, Days in range: 1)&lt;br /&gt;
setstate Forecast 2025-01-13 17:00:05 pvCorrectionFactor_17 0.28 (automatic - old factor: 0.52, Sun Alt range: 0, Cloud range: 45, Days in range: 2)&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 pvCorrectionFactor_Auto on_complex_ai&lt;br /&gt;
setstate Forecast 2024-10-30 19:42:19 setupStringAzimuth GarageSE=-55 GarageNW=125 HausNW=125 HausSW=35 HausSE1=-55 HausSE2=-55&lt;br /&gt;
setstate Forecast 2024-10-30 19:37:38 setupStringDeclination GarageSE=40 GarageNW=40 HausNW=50 HausSW=50 HausSE1=50 HausSE2=50&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_BatPowerIn_Sum 132 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_BatPowerOut_Sum 10 W&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_SunHours_Remain 0.00&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_SunMinutes_Remain 0&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_allStringsFullfilled 1&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_conForecastTillNextSunrise 9467 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_currentAPIinterval 0&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_currentRunMtsConsumer_01 132 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_currentRunMtsConsumer_02 184 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_currentRunMtsConsumer_03 329512 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_currentRunMtsConsumer_04 200 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_currentRunMtsConsumer_05 27 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_currentRunMtsConsumer_06 193 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_currentRunMtsConsumer_07 106 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_dayAfterTomorrowPVforecast 7344 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_daysUntilBatteryCare_01 12&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_daysUntilBatteryCare_02 12&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_lastretrieval_time 2025-01-13 18:53:54&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_lastretrieval_timestamp 1736790834&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_response_message success&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeAvgDayConsumer_01 545.94 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeAvgDayConsumer_02 382.10 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeAvgDayConsumer_03 1430.13 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeAvgDayConsumer_04 262.05 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeAvgDayConsumer_05 28.80 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeAvgDayConsumer_06 622.63 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeAvgDayConsumer_07 276.51 min&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeCentralTask 0.3744&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeLastAPIAnswer -&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeLastAPIProc -&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_runTimeTrainAI 1.2529&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayBatIn_01 3683.0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayBatIn_02 1617.0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayBatOut_01 2287.0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayBatOut_02 1057.0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayConForecastTillSunset 0 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 00:59:59 special_todayConsumptionForecast_01 509 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 01:59:58 special_todayConsumptionForecast_02 499 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 02:59:53 special_todayConsumptionForecast_03 501 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 03:59:52 special_todayConsumptionForecast_04 511 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 04:59:56 special_todayConsumptionForecast_05 503 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 05:59:59 special_todayConsumptionForecast_06 495 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 06:59:52 special_todayConsumptionForecast_07 504 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 07:59:50 special_todayConsumptionForecast_08 561 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 08:59:57 special_todayConsumptionForecast_09 768 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 09:59:58 special_todayConsumptionForecast_10 783 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 10:59:58 special_todayConsumptionForecast_11 692 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 11:59:49 special_todayConsumptionForecast_12 791 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 12:59:49 special_todayConsumptionForecast_13 910 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 13:59:49 special_todayConsumptionForecast_14 1022 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 14:59:49 special_todayConsumptionForecast_15 965 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 15:59:50 special_todayConsumptionForecast_16 812 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 16:59:49 special_todayConsumptionForecast_17 884 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 17:59:54 special_todayConsumptionForecast_18 928 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayConsumptionForecast_19 874 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayConsumptionForecast_20 763 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayConsumptionForecast_21 750 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayConsumptionForecast_22 765 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayConsumptionForecast_23 643 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayConsumptionForecast_24 601 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayDoneAPIcalls 0&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayDoneAPIrequests 4532&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayGridConsumption 5500.9 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayGridFeedIn 178.9 Wh&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayMaxAPIcalls n.a.&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayRemainingAPIcalls n.a.&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:54 special_todayRemainingAPIrequests n.a.&lt;br /&gt;
setstate Forecast 2025-01-13 18:53:55 state updated&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
So sollte es dann als Ergebnis aussehen:&lt;br /&gt;
[[Datei:Bildschirmfoto 2025-01-13.png|zentriert|mini|Ergebnis nach kompletter Einrichtung]]&lt;br /&gt;
[[Datei:Bildschirmfoto 2025-01-13 Teil II.png|alternativtext=Unterer Teil der Anzeige|zentriert|mini]]&lt;br /&gt;
&lt;br /&gt;
== Praxisbeispiele und Lösungsansätze für Steuerungen ==&lt;br /&gt;
=== Fallstudie: Trockner darf pausiert werden, wenn nicht genug PV-Überschuss vorhanden ist, soll aber nach spätestens X Stunden fertig sein ===&lt;br /&gt;
Für diese Aufgabenstellung sind mehrere Schlüssel essentiell wichtig, &#039;&#039;&#039;power&#039;&#039;&#039;, &#039;&#039;&#039;mintime&#039;&#039;&#039;, &#039;&#039;&#039;mode&#039;&#039;&#039; und &#039;&#039;&#039;interruptable&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Die Bedingung, dass der Trockner nach spätestens X Stunden fertig sein soll, steht in einem Widerspruch zu der Möglichkeit den Trockner bei zu wenig PV-Überschuss unterbrechen zu können. Im Extremfall könnte nach dem Start die gesamte Planungszeit ungenügender Überschuss vorliegen und der Trockner nicht arbeiten. Der Schlüssel &#039;&#039;&#039;mintime&#039;&#039;&#039; (in Minuten) wird nun wie folgt angesetzt:&lt;br /&gt;
&lt;br /&gt;
 mintime = 2,5 x &amp;lt;gewöhnliche Betriebszeit für einen Trockengang (mneed, z.B. 120)&amp;gt; = 300 Minuten&lt;br /&gt;
&lt;br /&gt;
Das bedeutet, der Trockner wird nach 300 Minuten Laufzeit abgeschaltet werden bzw. vom Netz getrennt. Der Trockenvorgang wird natürlich bereits nach 120 Minuten beendet sein, sofern nach Start keine Unterbrechnung des Vorganges erfolgte. Wurde der Vorgang unterbrochen, soll der Trockner innerhalb &lt;br /&gt;
&lt;br /&gt;
 X+120 bis X+300 &lt;br /&gt;
&lt;br /&gt;
fertig sein. &lt;br /&gt;
&lt;br /&gt;
Demnach ist die ausgeführte Laufzeit des Trockners permanent zu überwachen, zu summieren (rtsum) und die notwendige Restzeit (mneed - rtsum) mit der noch verbleibenden Restzeit (mrest) bis zur geplanten Abschaltung des Consumers zu vergleichen. Ist der Consumer gestartet, wird die Laufzeit in der pvHistory für jede Stunde des Tages im Schlüssel &#039;&#039;minutescsmXX&#039;&#039; (XX = Consumernummer) aufgezeichnet und kann darüber ausgewertet werden. Somit ist:&lt;br /&gt;
&lt;br /&gt;
 msum = for (01 .. 24) {Summe von minutescsmXX} des aktuellen Tages&lt;br /&gt;
&lt;br /&gt;
Solange die Bedingung &lt;br /&gt;
&lt;br /&gt;
 mrest &amp;gt;= (mneed - msum)&lt;br /&gt;
&lt;br /&gt;
zutrifft, darf der Trockner unterbrochen werden, anderenfalls darf er nicht mehr unterbrochen werden da die Restzeit evtl. nicht ausreichen könnte um den Vorgang abzuschließen. Für die Planung des Trockners wird der Consumer wie gewöhnlich nach dem Bedarf im Modul registriert. Für die Steuerung der Unterbrechbarkeit wird noch ein Code erstellt, welcher im Trockner-Device &#039;&#039;Dryerdev&#039;&#039; das Reading &#039;&#039;SF_Int&#039;&#039; zur Unterbrechnungssteuerung verwaltet. Dieses Reading wird dem Schlüssel &#039;&#039;&#039;interruptable&#039;&#039;&#039; übergeben. Für das Beispiel wird der Consumer &#039;07&#039; verwendet, es kann natürlich jede andere freie Nummer verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 attr &amp;lt;Name&amp;gt; consumer07 Dryerdev:Trockner&lt;br /&gt;
                        icon=scene_cloves_dryer type=dryer power=2000 mode=must mintime=300 &lt;br /&gt;
                        on=on off=off etotal=energy_Wh:Wh pcurr=power:W&lt;br /&gt;
                        auto=automatic asynchron=1&lt;br /&gt;
                        interruptable=Dryerdev:SF_Int:1&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Kombination &#039;&#039;&#039;auto=automatic&#039;&#039;&#039; eröffnet die Möglickeit, in der Consumer-Legende den Trockner-Start zusätzlich manuell freizugeben oder zu verbieten. &lt;br /&gt;
&lt;br /&gt;
Im nachfolgenden Code werden modulinterne Subroutinen verwendet. Diese sind inklusive dem Paket &#039;&#039;FHEM::SolarForecast::&#039;&#039; anzugeben. Die erstellte Subroutine wird in die 99_mySolarForecastUtils.pm eingefügt. Die Datei 99_mySolarForecastUtils.pm wird vorher von 99_myUtils.pm kopiert und enthält für die Übersichtlichkeit nur Routinen für SolarForecast.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
############################################################################&lt;br /&gt;
#   Trocknersteuerung (einfügen in 99_mySolarForecastUtils.pm) &lt;br /&gt;
############################################################################&lt;br /&gt;
sub dryControl {&lt;br /&gt;
  my $name  = shift;&lt;br /&gt;
  my $c     = shift;                                                              # Nummer des Verbrauchers, z.B. 07&lt;br /&gt;
  my $mneed = shift;                                                              # gewöhlich benötigte Zeit für &lt;br /&gt;
                                                                                  # einen Trockengang, z.B. 120 (Minuten)&lt;br /&gt;
  &lt;br /&gt;
  $c            = sprintf &amp;quot;%02d&amp;quot;, $c;                                             # falls führende 0 vergessen wird&lt;br /&gt;
  my $dryer     = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;name&#039;, &#039;&#039;);       # Devicename des Trockners&lt;br /&gt;
  my $plstate   = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;planstate&#039;, &#039;&#039;); &lt;br /&gt;
  my $simpCstat = FHEM::SolarForecast::simplifyCstate ($plstate);                 # akt. Status des Consumers&lt;br /&gt;
&lt;br /&gt;
  if ($simpCstat =~ /started|interrupt|continu/xs) {                              # Vorgang ist gestartet&lt;br /&gt;
      my $t       = time;&lt;br /&gt;
      my $startts = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;planswitchon&#039;,  &#039;&#039;);&lt;br /&gt;
      my $stopts  = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;planswitchoff&#039;, &#039;&#039;);&lt;br /&gt;
      return if(!$startts || !$stopts);&lt;br /&gt;
      &lt;br /&gt;
      my $mrest = sprintf &#039;%.0f&#039;, (($stopts - $t) / 60);                         # Restlaufzeit (Minuten)      &lt;br /&gt;
      my $dt     = FHEM::SolarForecast::timestringsFromOffset ($startts, 0);&lt;br /&gt;
      my $day    = $dt-&amp;gt;{day};&lt;br /&gt;
      my $hstart = int $dt-&amp;gt;{hour} + 1;                                          # lfd. Stunde bei Trockner-Start &lt;br /&gt;
      my $msum   = 0;&lt;br /&gt;
	  &lt;br /&gt;
      for my $hod (1..24) {                                                      # bisherige Laufzeit des Trockners&lt;br /&gt;
          next if($hod &amp;lt; $hstart);&lt;br /&gt;
          $hod = sprintf &amp;quot;%02d&amp;quot;, $hod;&lt;br /&gt;
          $msum += FHEM::SolarForecast::HistoryVal ($name, $day, $hod, &amp;quot;minutescsm${c}&amp;quot;, 0);&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      my $dhash = $defs{$dryer};&lt;br /&gt;
&lt;br /&gt;
      if ($mrest &amp;gt;= ($mneed - $msum)) {&lt;br /&gt;
          readingsSingleUpdate ($dhash, &#039;SF_Int&#039;, 1, 0);                        # Interrupt-Freigabe&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
          readingsSingleUpdate ($dhash, &#039;SF_Int&#039;, 0, 0);                        # keine Interrupt-Freigabe&lt;br /&gt;
  &lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Damit die Routine mit jedem Zyklus im Modul ausgeführt wird, legen wir sie im Attribut &#039;&#039;&#039;ctrlUserExitFn&#039;&#039;&#039; an. Es werden der Name des SolarForecast-Devices, die Verbrauchernummer (07) und die gewöhnliche Laufzeit des Trockners für einen Trockenvorgang (120 Minuten) übergeben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;Name&amp;gt; ctrlUserExitFn  {  &lt;br /&gt;
                               ::dryControl ($name, &#039;07&#039;, 120);&lt;br /&gt;
                            }&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Fallstudie: Umwälzpumpe Pool soll X Stunden am Tag laufen. Ist kein PV-Überschuß vorhanden, müssen die X Stunden/Tag trotzdem erreicht werden ===&lt;br /&gt;
&lt;br /&gt;
Die Vorgehensweise ähnelt der Fallstudie zuvor. Verändernd kommt hinzu, dass der Planungszeitraum bewusst sehr lang angesetzt wird mit der Option, den Zyklus des Consumers vorfristig zu beenden sobald die Laufzeit von X Stunden am Tag erreicht ist. &lt;br /&gt;
Im folgenden Beispiel soll die Pumpe 5 Stunden am Tag (300 Minuten) laufen und dabei mögliche PV-Überschüsse ausnutzen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 attr &amp;lt;Name&amp;gt; consumer07 Pump:Zirkulationspumpe&lt;br /&gt;
                        icon=sani_pump type=other power=50 mode=must mintime=780 notafter=09&lt;br /&gt;
                        on=on off=off etotal=energy_Wh:Wh pcurr=power:W&lt;br /&gt;
                        auto=automatic &lt;br /&gt;
                        interruptable=1 swoffcond=Pump:SF_Abort:1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Mit dieser Registrierung wird die Pumpe ca. 09 Uhr täglich starten. Wenn kein bzw. ungenügender PV-Überschuß vorhanden ist, erfolgt eine Unterbrechung nach dem Start. Ist genügender PV-Überschuß (wieder) vorhanden, erfolgt keine Unterbrechnung (mehr). Zusätzlich wird über den Schlüssel &#039;&#039;&#039;swoffcond&#039;&#039;&#039; eine vorfristige Beendigung veranlasst, sobald die gewünschte Tageslaufzeit erreicht ist.&lt;br /&gt;
&lt;br /&gt;
Zur Verwaltung des Schlüssels &#039;&#039;interruptable&#039;&#039; und des Readings &#039;&#039;SF_Abort&#039;&#039; verwenden wir eine leicht veränderte Subroutine aus dem vorhergehenden Fallbeispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
############################################################################&lt;br /&gt;
#   Pumpensteuerung (einfügen in 99_mySolarForecastUtils.pm) &lt;br /&gt;
############################################################################&lt;br /&gt;
sub pumpControl {&lt;br /&gt;
  my $name  = shift;&lt;br /&gt;
  my $c     = shift;                                                              # Nummer des Verbrauchers, z.B. 07&lt;br /&gt;
  my $mneed = shift;                                                              # Soll-Pumpenzeit, z.B. 300 (Minuten)&lt;br /&gt;
  &lt;br /&gt;
  $c            = sprintf &amp;quot;%02d&amp;quot;, $c;                                             # falls führende 0 vergessen wird&lt;br /&gt;
  my $pump      = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;name&#039;, &#039;&#039;);       # Devicename der Pumpe&lt;br /&gt;
  my $plstate   = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;planstate&#039;, &#039;&#039;);&lt;br /&gt;
  my $intbl     = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;interruptable&#039;, 1); &lt;br /&gt;
  my $simpCstat = FHEM::SolarForecast::simplifyCstate ($plstate);                 # akt. Status des Consumers&lt;br /&gt;
  my $dhash     = $defs{$pump};&lt;br /&gt;
&lt;br /&gt;
  readingsSingleUpdate ($dhash, &#039;SF_Abort&#039;, 0, 0);                                # default keine Zyklusbeendigung&lt;br /&gt;
&lt;br /&gt;
  if ($simpCstat =~ /started|interrupt|continu/xs) {                              # Vorgang ist gestartet&lt;br /&gt;
      my $t       = time;&lt;br /&gt;
      my $startts = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;planswitchon&#039;,  &#039;&#039;);&lt;br /&gt;
      my $stopts  = FHEM::SolarForecast::ConsumerVal ($name, $c, &#039;planswitchoff&#039;, &#039;&#039;);&lt;br /&gt;
      return if(!$startts || !$stopts);&lt;br /&gt;
      &lt;br /&gt;
      my $mrest = sprintf &#039;%.0f&#039;, (($stopts - $t) / 60);                         # Restlaufzeit (Minuten)      &lt;br /&gt;
      my $dt     = FHEM::SolarForecast::timestringsFromOffset ($startts, 0);&lt;br /&gt;
      my $day    = $dt-&amp;gt;{day};&lt;br /&gt;
      my $hstart = int $dt-&amp;gt;{hour} + 1;                                          # lfd. Stunde bei Pumpen Start &lt;br /&gt;
      my $msum   = 0;&lt;br /&gt;
	  &lt;br /&gt;
      for my $hod (1..24) {                                                      # bisherige Laufzeit der Pumpe&lt;br /&gt;
          next if($hod &amp;lt; $hstart);&lt;br /&gt;
          $hod = sprintf &amp;quot;%02d&amp;quot;, $hod;&lt;br /&gt;
          $msum += FHEM::SolarForecast::HistoryVal ($name, $day, $hod, &amp;quot;minutescsm${c}&amp;quot;, 0);&lt;br /&gt;
      }&lt;br /&gt;
     &lt;br /&gt;
      if ($msum &amp;gt;= $mneed) {&lt;br /&gt;
          readingsSingleUpdate ($dhash, &#039;SF_Abort&#039;, 1, 0);                       # vorfristige Zyklusbeendigung&lt;br /&gt;
          return;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ($mrest &amp;gt;= ($mneed - $msum)) {                        &lt;br /&gt;
          fhem (&amp;quot;set $name attrKeyVal consumer$c interruptable=1&amp;quot;) if(!$intbl);  # Interrupt-Freigabe&lt;br /&gt;
      }&lt;br /&gt;
      else {                        &lt;br /&gt;
          fhem (&amp;quot;set $name attrKeyVal consumer$c interruptable=0&amp;quot;) if($intbl);   # keine Interrupt-Freigabe&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Befehl &#039;&#039;attrKeyVal&#039;&#039; setzt den Schlüssel &#039;&#039;interruptable&#039;&#039; im Attribut &#039;&#039;consumer07&#039;&#039; dynamisch. Diese Strukturänderung wird automatisch im System gespeichert, sofern &#039;&#039;global-&amp;gt;autosave=1&#039;&#039; gesetzt ist. Diese Einstellung ist der FHEM default.  &lt;br /&gt;
&lt;br /&gt;
Damit die Routine mit jedem Zyklus im Modul ausgeführt wird, legen wir sie im Attribut &#039;&#039;&#039;ctrlUserExitFn&#039;&#039;&#039; an. Es werden der Name des SolarForecast-Devices, die Verbrauchernummer (07) und die Mindestlaufzeit der Umwälzpumpe (300 Minuten) übergeben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;Name&amp;gt; ctrlUserExitFn  {  &lt;br /&gt;
                               ::pumpControl ($name, &#039;07&#039;, 300);&lt;br /&gt;
                            }&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Dynamische Ladestromsteuerung eines Victron MultiPlus II Chargers mit Pylontech Batterie ===&lt;br /&gt;
Die nachfolgend beschriebene Lösung soll als Anregung für eigene Optimierungen verstanden werden und ist weiter ausbaufähig bzw. erweiterbar um zum Beipiel jahreszeitliche Anpassungen des Verfahrens.&lt;br /&gt;
&lt;br /&gt;
Die MultiPlus II Batteriewechselrichter von Victron sollten die Batterien nicht mit dem maximal möglichen Ladestrom beladen, da sich die Effizienz deutlich verschlechtert wenn sich die Ströme den jeweiligen Grenzwerten nähern ([https://www.victronenergy.com/upload/documents/Output-rating-operating-temperature-and-efficiency.pdf Lesestoff]). &lt;br /&gt;
Andererseits sollen die Ladeleistungen so gewählt werden, dass die verfügbare Solarenergie eine volle Aufladung der Akkus ermöglicht.&lt;br /&gt;
&lt;br /&gt;
Das Victron System ermöglicht das Auslesen und die Steuerung der Anlage via MQTT. Die Einbindung in FHEM über MQTT soll hier nicht beschrieben werden.&lt;br /&gt;
Aber wenn dies erfolgt ist, kann der Ladestrom z.B. mit dem Kommando:&lt;br /&gt;
&lt;br /&gt;
 set &amp;lt;Device&amp;gt; MaxChargeCurrent &amp;lt;Wert&amp;gt;&lt;br /&gt;
&lt;br /&gt;
eingestellt bzw. variiert werden.&lt;br /&gt;
&lt;br /&gt;
Das Modul bietet dem Nutzer die Möglichkeit in dem Attribut &#039;&#039;ctrlUserExitFn&#039;&#039; eigenen Code zur Auführung zu bringen. Der in diesem Attribut enthaltene Code wird am Ende jedes Zyklus (siehe Attribut &#039;&#039;plantControl-&amp;gt;cycleInterval&#039;&#039;) ausgeführt.&lt;br /&gt;
In dem Attribut wird dieser Code eingetragen dessen Funktion und Zusammenspiel nachfolgend erläutert wird:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{ &lt;br /&gt;
  ## Vars&lt;br /&gt;
  ########&lt;br /&gt;
  my $batdev  = (split &amp;quot; &amp;quot;, ReadingsVal ($name, &#039;currentBatteryDev&#039;, &#039;&#039;))[0];  # Batteriedevice&lt;br /&gt;
  my $vebus   = &#039;MQTT2_cerboGX_c0619ab34e08_vebus&#039;;                            # Victron Vebus Device &lt;br /&gt;
  my $mppt1   = &#039;MQTT2_cerboGX_c0619ab34e08_solarcharger_Common&#039;;              # SmartLoader Device&lt;br /&gt;
  my $maxcspc = 105;                        # max. Ladesollstrom (A) nominal&lt;br /&gt;
  my $spcorr  = 0.9;                        # Korrekturfaktor Überschuss 90%&lt;br /&gt;
  my $sch     = 0.25;                       # Verkürzungsfaktor Zeit bis Vollladung vor Sunset             &lt;br /&gt;
  &lt;br /&gt;
  my $cclvl0  = 15;                         # Ladestrom Level 0 max. 5  A (240 W) pro WR  (720  W)&lt;br /&gt;
  my $cclvl1  = 27;                         # Ladestrom Level 1 max. 9  A (432 W) pro WR  (1296 W)&lt;br /&gt;
  my $cclvl2  = 54;                         # Ladestrom Level 2 max. 18 A (864 W) pro WR  (2592 W)&lt;br /&gt;
  my $cclvl3  = 81;                         # Ladestrom Level 3 max. 27 A (1296 W) pro WR (3888 W)&lt;br /&gt;
  ###############&lt;br /&gt;
  &lt;br /&gt;
  my $cofc   = ReadingsNum ($name,  &#039;special_todayConForecastTillSunset&#039;, 0);&lt;br /&gt;
  my $pvfc   = ReadingsNum ($name,  &#039;RestOfDayPVforecast&#039;, 0);&lt;br /&gt;
  my $cpv    = ReadingsNum ($name,  &#039;Current_PV&#039;,          0);&lt;br /&gt;
  my $mppt1c = ReadingsNum ($mppt1, &#039;DC_0_Current_value&#039;,  0);             # Load I MPPT1 (max. 44 A)&lt;br /&gt;
  my $ssts   = CurrentVal  ($hash,  &#039;sunsetTodayTs&#039;,        0);&lt;br /&gt;
  my $srts   = CurrentVal  ($hash,  &#039;sunriseTodayTs&#039;,       0);&lt;br /&gt;
  my $sdiff  = ($ssts - $srts) / 3600;                                     # Sonnengang heute in h &lt;br /&gt;
  &lt;br /&gt;
  my $solh   = ReadingsNum ($name, &#039;special_SunHours_Remain&#039;, 0);          # h bis SunSet&lt;br /&gt;
  $solh      = $solh - ($sdiff * $sch);                                    # h bis SunSet - X&lt;br /&gt;
  $solh      = $solh &amp;lt; 0 ? 0 : $solh;&lt;br /&gt;
   &lt;br /&gt;
  my $ahrem  = ReadingsNum ($batdev, &#039;EnergyRemain&#039;, 0) / 48;              # Ah Bat bis SOC&lt;br /&gt;
  $ahrem     = sprintf &amp;quot;%.0f&amp;quot;, $ahrem;&lt;br /&gt;
  &lt;br /&gt;
  my $fcdiff = ($pvfc - $cofc) * $spcorr;     # Korrektur d. noch prognostizierten Überschussenergie &lt;br /&gt;
  $fcdiff    = $fcdiff &amp;lt; 0 ? 0 : $fcdiff;     # Wh&lt;br /&gt;
  &lt;br /&gt;
  my $loadcur = $maxcspc;&lt;br /&gt;
  &lt;br /&gt;
  if ($cpv &amp;amp;&amp;amp; $solh &amp;amp;&amp;amp; $ahrem &amp;amp;&amp;amp; $fcdiff &amp;gt; $ahrem) {  # Ladeanforderung und Überschuss übersteigt &lt;br /&gt;
                                                      # Lademenge&lt;br /&gt;
      my $cspc = sprintf &amp;quot;%.2f&amp;quot;, ($ahrem / $solh);    # A = Ah / h -&amp;gt; A Soll Schätzung&lt;br /&gt;
&lt;br /&gt;
      Log3 ($name, 3, qq{$name - userFn -&amp;gt; Battery charging process should be completed }.&lt;br /&gt;
                      qq{in &amp;gt;$solh&amp;lt; hours});&lt;br /&gt;
      Log3 ($name, 3, qq{$name - userFn -&amp;gt; raw Target charging current: $cspc});&lt;br /&gt;
	  &lt;br /&gt;
      $cspc = sprintf &amp;quot;%.2f&amp;quot;, ($cspc - $mppt1c);      # SmartLoader IST berücksichtigen&lt;br /&gt;
	  &lt;br /&gt;
      Log3 ($name, 3, qq{$name - userFn -&amp;gt; Charge Current minus MPPT load Current &amp;gt;$mppt1c&amp;lt;: $cspc});&lt;br /&gt;
	  &lt;br /&gt;
      $loadcur = $cspc &amp;lt;= 0       ? 0       :     # nur MPPT Ladung&lt;br /&gt;
                 $cspc &amp;lt;= $cclvl0 ? $cclvl0 :     # Übergangsbereich&lt;br /&gt;
                 $cspc &amp;lt;= $cclvl1 ? $cclvl1 :     # Ladung mit Level 1 Leistung&lt;br /&gt;
                 $cspc &amp;lt;= $cclvl2 ? $cclvl2 :     # Ladung mit Level 2 Leistung&lt;br /&gt;
                 $cspc &amp;lt;= $cclvl3 ? $cclvl3 :     # Ladung mit Level 3 Leistung&lt;br /&gt;
                 $maxcspc;                        # max. Ladesollstrom&lt;br /&gt;
	  &lt;br /&gt;
	  Log3 ($name, 3, qq{$name - userFn -&amp;gt; MaxChargeCurrent calculated: $loadcur});&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  readingsSingleUpdate ($hash, &#039;userFn_Bat_MaxChargeCurrent&#039;,       $loadcur.&#039; A&#039;,          1);&lt;br /&gt;
  readingsSingleUpdate ($hash, &#039;userFn_Bat_HoursUntilChargeFinish&#039;, sprintf (&amp;quot;%.2f&amp;quot;,$solh), 1);&lt;br /&gt;
  CommandSet           (undef, &amp;quot;$vebus MaxChargeCurrent $loadcur&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2023-05-03 201457.png|right|thumb|300px|Vorhersage PV Erzeugung und Energieverbrauch des (restlichen) Tages]]&lt;br /&gt;
Zunächst wird der am aktuellen Tag zu erwartende Totalüberschuss solarer Energie ermittelt. Das erfolgt aus der Differenz von &#039;&#039;RestOfDayPVforecast&#039;&#039; und &#039;&#039;RestOfDayConsumptionForecast&#039;&#039; unter Berücksichtigung eines Sicherheitsabschlages. Das Ergebnis liegt in Wh vor und wird bezogen auf 48 V (die Spannung des Batteriesystems) in Ah umgerechnet. &lt;br /&gt;
&lt;br /&gt;
Die noch benötigte Ladung der Batterie zur Erreichung des Soll SOC-Wertes ermittelt das ReadingsNum &#039;&#039;special_SunHours_Remain&#039;&#039; aus dem Batteriedevice, welches vom Modulreading &#039;&#039;currentBatteryDev&#039;&#039; ausgelesen wird.&lt;br /&gt;
&lt;br /&gt;
Sofern der so ermittelte solare Überschuss des (Rest)Tages die benötigte Lademenge der Batterie übersteigt, wird der DC Ladestrom so verringert, dass er sich in einem effizienten Bereich des MultiPlus II bewegt und dennoch rechnerisch ausreicht auf den Soll SOC-Wert zu laden.  &lt;br /&gt;
&lt;br /&gt;
Zu diesem Zweck wird im Modul über die Auswahl von &#039;&#039;SunHours_Remain&#039;&#039; im Attribut &#039;&#039;ctrlStatisticReadings&#039;&#039; das zusätzliche Reading &#039;&#039;special_SunHours_Remain&#039;&#039; erzeugt. Aus dem (noch) erforderlichen Ladungswert &#039;&#039;&#039;EnergyRemain&#039;&#039;&#039; (Ah) und der um eine Sicherheitszeit (&#039;&#039;Verkürzungsfaktor Zeit bis Vollladung vor Sunset&#039;&#039;) reduzierte Zeit bis zum Sonnenuntergang wird die rechnerisch erforderliche mindest Ladestromstärke ermittelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dieser Wert wird auf die zu Anfang des Codeblocks definierten Ladelevel (&#039;&#039;Ladestrom Level X&#039;&#039;) abstrahiert und ein passender &#039;&#039;&#039;effizienter&#039;&#039;&#039; Ladestrom-Bereich ausgewählt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Screenshot 2023-05-03 201719.png|right|thumb|300px|Vorhersage PV Erzeugung und Energieverbrauch des (restlichen) Tages]]&lt;br /&gt;
&lt;br /&gt;
In allen anderen Fällen, wenn z.B. rechnerisch nicht genügend oder keine Solarenergie erzeugt wird (Nachts) oder der Speicher gefüllt ist, wird der maximale (Sollwert) des Ladestroms &#039;&#039;$maxcspc = 35&#039;&#039; eingestellt. Dieser Wert entspricht dem maximalen Ladestrom gemäß Herstellerunterlagen Pylontech.&lt;br /&gt;
&lt;br /&gt;
Die Einstellung erfolgt per Set-Kommando im Victron vebus MQTT Device &#039;&#039;MQTT2_cerboGX_c0619ab34e08_vebus&#039;&#039;. Dieses Device wurde auch während der MQTT Intergration des Victron Systems angelegt und wird hier nicht näher diskutiert.&lt;br /&gt;
Zur Kontrolle des kalkulierten Wertes werden noch die Readings &#039;&#039;SolCast_userFn_MaxChargeCurrent&#039;&#039; im Batteriedevice und &#039;&#039;userFn_Bat_MaxChargeCurrent&#039;&#039; im SolarForecast Device generiert.&lt;br /&gt;
&lt;br /&gt;
Neben der Effizienzoptimierung hat dieses Verfahren auch den Vorteil einer möglichst schonenden Batterieaufladung was der Lebensdauer der Komponenten zugute kommen sollte.&lt;br /&gt;
&lt;br /&gt;
=== Poolheizung in Abhängigkeit von vorhandenem Photovoltaik-Überschuss aktivieren/deaktivieren (Eigenverbrauch optimieren) ===&lt;br /&gt;
Eine (Whirl-)Poolheizung soll bei ausreichend überschüssiger Energie aus einer Photovoltaikanlage dynamisch gesteuert werden (Eigenverbrauchsoptimierung). Im folgenden Beispiel ist ein Device mit der Bezeichnung &#039;&#039;Forecast&#039;&#039; des Typs &#039;&#039;SolarForecast bereits&#039;&#039; angelegt und konfiguriert. Ein freies Consumer-Attribut &#039;&#039;consumer01&#039;&#039; wird beispielhaft wie folgt definiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr Forecast consumer01 MQTT2_layzspa type=other power=1950 mode=can on=&amp;quot;heater on&amp;quot; off=&amp;quot;heater off&amp;quot; pcurr=WATT interruptable=1 swstate=heaterstate:1:0 auto=Automatiksteuerung mintime=SunPath spignorecond=EVCharger22:LadungMitPVUeberschussActive:1 icon=scene_pool notbefore=8 notafter=20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Poolheizung ist innerhalb FHEM bereits im Device &#039;&#039;MQTT2_layzspa&#039;&#039; integriert und lässt sich normalerweise per FHEM-Befehl &amp;quot;set &#039;&#039;MQTT2_layzspa heater on&amp;quot;&#039;&#039; oder &#039;&#039;&amp;quot;set MQTT2_layzspa heater off&amp;quot;&#039;&#039; steuern. Entsprechend werden die Schlüssel-Wert-Paare &#039;&#039;&amp;quot;on=&amp;quot;&#039;&#039; und &#039;&#039;&amp;quot;off=&amp;quot;&#039;&#039; definiert. &lt;br /&gt;
&lt;br /&gt;
Der Stromverbrauch einer aktiven Poolheizung ist uns bekannt und wird im Schlüssel &#039;&#039;&amp;quot;power=1950&amp;quot;&#039;&#039; definiert. Die Heizfunktion soll nur bei ausreichend Überschuss aktiviert werden dürfen und muss bei Unterschreitung sofort abgeschaltet werden. Diese Eigenschaften werden über die Schlüssel &#039;&#039;&amp;quot;mode=can&amp;quot;&#039;&#039; und &#039;&#039;&amp;quot;interruptable=&amp;quot;1&amp;quot;&#039;&#039; festgelegt. Zuletzt soll grundsätzlich nur in der Zeit zwischen 08:00 Uhr bis 20:00 Uhr geheizt werden dürfen. &lt;br /&gt;
&lt;br /&gt;
Die Schlüssel &#039;&#039;&amp;quot;notbefore=8&amp;quot;&#039;&#039; und &#039;&#039;&amp;quot;notafter=20&amp;quot;&#039;&#039; legen diese Eigenschaften fest. Zur Anzeige des Schaltzustands der Heizung wurde der Schlüssel &#039;&#039;&amp;quot;swstate=heaterstate:1:0&amp;quot;&#039;&#039; hinzugefügt (Wert 1 = Heizung aktiv, Wert 0= Heizung aus). &lt;br /&gt;
&lt;br /&gt;
==== Priorisierung vor externen PV-Überschuss gesteuerten Verbrauchern ====&lt;br /&gt;
Der Schlüssel-Wert &#039;&#039;&#039;&amp;quot;spignorecond=&amp;quot;&#039;&#039;&#039; bietet optional die Möglichkeit, einen Consumer auch ohne ausreichend PV-Überschuss einzuschalten. Bei erfüllter Bedingung wird der Verbraucher entsprechend der Planung eingeschaltet auch wenn zu dem Zeitpunkt kein PV Überschuss vorliegt. Im obigen Beispiel existiert in der Haus-Elektroinstallation eine Elektroauto-Ladestation mit eigener (externer) dynamischer PV-gesteuerter Fahrzeugladung ohne Integration in &#039;&#039;SolarForecast&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
In Abhängigkeit von PV-Generatorleistung und Sonneneinstrahlung besteht manchmal eine Möglichkeit, dass der gesamte PV-Überschuss zur Fahrzeugladung genutzt wird. Das Modul SolarForecast würde entsprechend keinen (oder zu wenig) PV-Überschuss erkennen und den Consumer &#039;&#039;MQTT2_layzspa&#039;&#039; nicht aktivieren. &lt;br /&gt;
&lt;br /&gt;
Durch Verwendung von &#039;&#039;spignorecond=EVCharger22:LadungMitPVUeberschussActive:1&#039;&#039; wird die Poolheizung gegenüber der Ladestation jedoch priorisiert mit PV-Überschuss versorgt. In diesem Beispiel wird Consumer1 aktiviert, wenn die Ladestation &amp;quot;&#039;&#039;EVCharger22&#039;&#039;&amp;quot; sich im PV-Überschuss-Modus befindet &#039;&#039;&#039;und&#039;&#039;&#039; momentan ein Fahrzeug lädt. In der Folge würde der Hausstromverbrauch um ca. 1950 Watt steigen, die externe Fahrzeugladestation diese Änderung registrieren und die Fahrzeugladeleistung entsprechend verringern oder ganz abbrechen.&lt;br /&gt;
&lt;br /&gt;
=== Luftentfeuchter PV-Überschuss gesteuert mit externer Zwangs-Einschaltbedingung ===&lt;br /&gt;
Im folgenden Beispiel wird ein Luftentfeuchter normalerweise nur bei ausreichend PV-Überschuss Energie eingeschaltet (Eigenverbrauchsoptimierung), soll jedoch bei hoher Luftfeuchtigkeit zusätzlich aktiviert werden.&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr Forecast SolarForecast ShellyPlug1 type=other power=300 mode=can on=on off=off pcurr=power:W interruptable=1 swstate=state:on:off mintime=SunPath locktime=900 notbefore=07 notafter=22 spignorecond=ESPEasy_ESP_Easy1_am2302_sensor:humidity:high icon=Ventilator_fett&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Das in FHEM bereits angelegte Device namens &#039;&#039;ShellyPlug1&#039;&#039; dient als Zwischenstecker zur Steuerung des Luftentfeuchters. Der Schlüsselwert &amp;quot;&#039;&#039;mintime=SunPath&#039;&#039;&amp;quot; definiert die zeitliche Einplanung von Sonnenauf- bis untergang. Jedoch soll das Gerät unter keinen Umständen in der Nacht eingeschaltet werden, was über die Schlüssel &#039;&#039;&amp;quot;notbefore=07&amp;quot;&#039;&#039; bzw. &#039;&#039;&amp;quot;notafter=22&amp;quot;&#039;&#039; festgelegt ist. Weiterhin wurde im Schlüssel &#039;&#039;&amp;quot;locktime=900&amp;quot;&#039;&#039; festgelegt, dass zwischen (möglichen) Ein- und Ausschaltintervallen wenigstens 15 Minuten Wartezeit vergehen soll.&lt;br /&gt;
&lt;br /&gt;
Der Schlüssel-Wert &#039;&#039;&#039;&amp;quot;spignorecond=&amp;quot;&#039;&#039;&#039; bietet optional die Möglichkeit, ein Consumer bei erfüllter Bedingung auch ohne ausreichend PV-Überschuss einzuschalten. In diesem Beispiel wird ein Raumluftsensor abgefragt. Bei Vorhandensein der Einschaltschwelle &amp;quot;high&amp;quot; aktiviert sich der Consumer zur geplanten Laufzeit zwischen 07:00 bis 22:00 Uhr solange, bis der Raumluftsensor einen abweichenden Wert meldet.&lt;br /&gt;
&lt;br /&gt;
=== PV-Vorhersage an evcc übertragen (PV-Überschussladen von E-Autos über steuerbare Wallboxen) ===&lt;br /&gt;
[https://evcc.io evcc] ist ein Energie-Management-System, mit dem u.a. Überschussladen von Elektroautos möglich ist. Hier ist beschreiben, wie die SolarForecast-Vorhersagedaten per API von evcc abgerufen werden.&lt;br /&gt;
Lege im SolarForecast-Device ein User-Reading mit dem Namen forecast_json an. Es wandelt die Forecast-Werte in JSON und ins UTC-Zeitformat um, so dass evcc die Daten einlesen kann. Der Code wird nur ausgeführt, wenn das Reading nextCycletime ein Event erzeugt.&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
forecast_json:nextCycletime.* {&lt;br /&gt;
&lt;br /&gt;
    use strict;&lt;br /&gt;
    use warnings;&lt;br /&gt;
    use JSON;&lt;br /&gt;
    use DateTime;&lt;br /&gt;
    use DateTime::Format::Strptime;&lt;br /&gt;
&lt;br /&gt;
    my $hour = 0;&lt;br /&gt;
    my $last_start_ts;    &lt;br /&gt;
    my @output;&lt;br /&gt;
    &lt;br /&gt;
    # Parser für Datum mit CET/CEST&lt;br /&gt;
    my $parser = DateTime::Format::Strptime-&amp;gt;new(&lt;br /&gt;
        pattern   =&amp;gt; &amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,&lt;br /&gt;
        time_zone =&amp;gt; &amp;quot;Europe/Berlin&amp;quot;,&lt;br /&gt;
    );&lt;br /&gt;
&lt;br /&gt;
    # Alle NextHour-Daten durchsuchen (als Fallback zur Vermeidung einer Endlosschleife auf max. 100 Std. begrenzen)&lt;br /&gt;
    while ($hour &amp;lt; 100) {&lt;br /&gt;
        my $hour_str = sprintf(&amp;quot;NextHour%02d&amp;quot;, $hour);&lt;br /&gt;
        my $start_str = FHEM::SolarForecast::NexthoursVal(&amp;quot;$NAME&amp;quot;, $hour_str, &amp;quot;starttime&amp;quot;, &amp;quot;na&amp;quot;);&lt;br /&gt;
        my $pvfc = FHEM::SolarForecast::NexthoursVal(&amp;quot;$NAME&amp;quot;, $hour_str, &amp;quot;pvfc&amp;quot;, &amp;quot;na&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        # Schleife beenden, wenn keine Vorhersage-Werte mehr vorhanden sind&lt;br /&gt;
        last if $start_str eq &amp;quot;na&amp;quot; or $pvfc eq &amp;quot;na&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        my $start_ts = $parser-&amp;gt;parse_datetime($start_str);  # Zeitzone und Sommer/Winterzeit berücksichtigen&lt;br /&gt;
        $last_start_ts = $start_ts-&amp;gt;clone;  # merken für spätere Prüfung&lt;br /&gt;
&lt;br /&gt;
        # Zeit in UTC und ISO 8601 konvertieren und Vorhersagewert ergänzen&lt;br /&gt;
        push @output, {&lt;br /&gt;
            start =&amp;gt; $start_ts-&amp;gt;set_time_zone(&amp;quot;UTC&amp;quot;)-&amp;gt;iso8601() . &amp;quot;Z&amp;quot;,&lt;br /&gt;
            end   =&amp;gt; $start_ts-&amp;gt;add(hours =&amp;gt; 1)-&amp;gt;set_time_zone(&amp;quot;UTC&amp;quot;)-&amp;gt;iso8601() . &amp;quot;Z&amp;quot;,&lt;br /&gt;
            value =&amp;gt; 0 + $pvfc,&lt;br /&gt;
        };&lt;br /&gt;
&lt;br /&gt;
        $hour++;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Prüfen, ob der letzte Start-Wert 23 Uhr war -&amp;gt; zusätzliche Stunde anhängen, damit der Tag&lt;br /&gt;
    # bei evcc als vollständig angezeigt wird (siehe https://github.com/evcc-io/evcc/issues/22979)&lt;br /&gt;
    if (defined $last_start_ts &amp;amp;&amp;amp; $last_start_ts-&amp;gt;hour == 23) {&lt;br /&gt;
&lt;br /&gt;
        # Zeit in UTC und ISO 8601 konvertieren und Vorhersagewert ergänzen&lt;br /&gt;
        push @output, {&lt;br /&gt;
            start =&amp;gt; $last_start_ts-&amp;gt;add(hours =&amp;gt; 1)-&amp;gt;set_time_zone(&amp;quot;UTC&amp;quot;)-&amp;gt;iso8601() . &amp;quot;Z&amp;quot;,&lt;br /&gt;
            end   =&amp;gt; $last_start_ts-&amp;gt;add(hours =&amp;gt; 1)-&amp;gt;set_time_zone(&amp;quot;UTC&amp;quot;)-&amp;gt;iso8601() . &amp;quot;Z&amp;quot;,&lt;br /&gt;
            value =&amp;gt; 0,&lt;br /&gt;
        };       &lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Ausgabe als JSON&lt;br /&gt;
    my $json = JSON-&amp;gt;new-&amp;gt;utf8-&amp;gt;pretty-&amp;gt;encode(\@output);&lt;br /&gt;
    return $json;&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[Datei:Screenshot 2025-08-04 104835.png|right|thumb|300px|Darstellung der PV-Vorhersage in evcc auf Basis der SolarForecast-Daten]]&lt;br /&gt;
Die Kommunikation läuft über den integrierten FHEM-Webserver, der die JSON-Daten aus dem Reading forecast_json veröffentlicht. Dazu wird in FHEM eine separate Webinstanz auf einem neuen Port erstellt, die ohne [[CsrfToken-HowTo|CSFR-Token]] (Achtung, Auswirkungen auf die Sicherheit beachten!) und nur von localhost aufrufbar ist (Voraussetzung: evcc läuft auf derselben Maschine wie FHEM):&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
define WEBapi FHEMWEB 8089&lt;br /&gt;
attr WEBapi csrfToken none&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;In der [https://docs.evcc.io/docs/tariffs#pv-vorhersage evcc-Konfig unter tariffs] steht die Quelle für die Solar-Vorhersage. Der FHEM-Befehl {ReadingsVal(&#039;solarforecast_dev&#039;,&#039;forecast_json&#039;,&#039;[]&#039;)} wird encoded und in die uri eingetragen. Trage statt solarforecast_dev den passenden Namen deines SolarForecast-Device ein. evcc holt die Daten aus dem Reading forecast_json dann regelmäßig ab.&amp;lt;syntaxhighlight lang=&amp;quot;yaml&amp;quot;&amp;gt;&lt;br /&gt;
solar:&lt;br /&gt;
  type: custom&lt;br /&gt;
  interval: 20m    &lt;br /&gt;
  forecast:&lt;br /&gt;
    source: http&lt;br /&gt;
    uri: http://localhost:8089/fhem?cmd=%7BReadingsVal%28%27solarforecast_dev%27%2C%27forecast_json%27%2C%27%5B%5D%27%29%7D&amp;amp;XHR=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Zur Verbesserung der Sicherheit kann das neue WEBapi Device auch ein [[CsrfToken-HowTo#csrfToken festlegen|festes CSFR-Token]] verwenden. Dieses Token muss dann in der evcc-Konfiguration an die uri mittels &amp;amp;fwcsrf=&amp;lt;festes token&amp;gt; angehängt werden.&lt;br /&gt;
&lt;br /&gt;
=== Poolsteuerung inkl. Eigenverbrauchsoptimierung und zusätzlichem Verbraucher, um Einspeiselimit zu vermeiden + manuelle Steuerung durch Sonderprogramme ===&lt;br /&gt;
&#039;&#039;&#039;Vorhandene Hardware:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Poolpumpe, die über mehrere Stufen verfügt. Hier werden zwei verwendet.&lt;br /&gt;
** Pump Low: Energiesparprogramm um den Pool umzuwälzen, auch wenn nicht genug PV Leistung zur Verfügung steht. -&amp;gt; ca. 250W&lt;br /&gt;
** Pump High: Normalbetrieb wenn genug PV Überschuss zur Verfügung steht. -&amp;gt; ca. 800W zusätzlich&lt;br /&gt;
* Poolheizung: Wärmepumpe -&amp;gt; ca. 1,5kW elektrisch&lt;br /&gt;
* Nachbars Pool: Pumpe + Salzanlage +pH-Regler -&amp;gt; ca. 650W&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Anforderungen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Pool muss täglich mindestens 1x umgewälzt werden.&lt;br /&gt;
* Im Idealfall wird der Pool &amp;gt; 2x umgewälzt.&lt;br /&gt;
* Bei genügend Überschuss soll der Pool geheizt werden. Es soll nur Sonnenstrom für die Poolheizung verwendet werden.&lt;br /&gt;
* Um das Einspeiselimit zu vermeiden, soll auch geheizt werden, wenn der Pool schon warm genug ist. (&amp;gt;28,5 °C)&lt;br /&gt;
* Sollte trotzdem noch das Einspeiselimit erreicht werden, soll auch der Pool des Nachbarn versorgt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Herausforderungen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Verbraucher dürfen nur in einer gewissen Reihenfolge geschaltet werden, auch wenn der Überschuss eine andere Reihenfolge ergeben würde.&lt;br /&gt;
* Pumpe mit mehreren Stufen muss als zwei Consumer abgebildet werden, um die Schaltlogik in SolarForecast abzubilden. Würde die Trennung außerhalb von Solarforecast realisiert, könnte Solarforecast den Verbraucher schlechter &amp;quot;lernen&amp;quot;, denn dann wäre es ein Consumer mit ständig wechselndem Verbrauch.&lt;br /&gt;
* Pool des Nachbarn darf nicht laut der benötigten Leistung geschaltet werden, sondern in Abhängigkeit der Einspeisebegrenzung.&lt;br /&gt;
&lt;br /&gt;
In diesem Solarforcast-Device sind 16 Verbraucher definiert. Im Weiteren werden jedoch nur die verwendeten betrachtet. Da diese Reglung mittlerweile recht komplex ist, werden auch viele Dinge, die nicht SolarForcast betreffen, aber trotzdem dazu gehören, gezeigt. Es trägt zum Verständnis bei und man kann sich hier sicher Ideen holen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ergebnis:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In den folgenden beiden Bildern sieht man das Regelverhalten recht gut.&lt;br /&gt;
[[Datei:Mustertag ohne Wolken.jpg|zentriert|mini|823x823px|Wolkenloser Tag]]&lt;br /&gt;
Hier erkennt man gut das Einschalten der jeweiligen Verbraucher (Pump Low, Pump High, Wärmepumpe und Nachbar Pool). Die Wärempumpe schaltet erst bei 5kW Überschuss ein, da der Pool eigentlich schon warm genug ist. Sie dient &amp;quot;nur&amp;quot; dazu, das EInspeiselimit zu verhindern.Auch das Auschalten ist wieder deutlich zu erkennen. Die Regleung folgt dem Überschuss recht gut. (Die Leistungsspitzen auf dem Plateau sind normale Großverbraucher im Haus.)&lt;br /&gt;
[[Datei:Wechselhaft.jpg|zentriert|mini|818x818px|Wechselhafter Tag.]]&lt;br /&gt;
In diesem Bild sieht man das &amp;quot;verzweifelte&amp;quot; Regeln um das wechselhafte Wetter auszugleichen. Hier gibt es keine korrekte Reaktion und es wird durch die Sperrzeiten und andere Delays versucht, ein zu häufiges hin- und herschalten möglichst zu vermeiden. In Summe aber auch bei so einem Wetter ein akzeptables Verhalten.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Definition der Consumer:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Allgemeine Erklärungen zu den Definitionen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;asynchron=0&#039;&#039;&#039;: Da hier die Werte des Überschusses für das Timing der Schaltvorgänge verwendet werden, darf hier keine Eventsteuerung verwendet werden. Sonst kann man nicht mehr sagen, über welchen Zeitraum der Mittelwert oder der Median gebildet wurde. Dafür wurde die Zykluszeit des SolarForecast-Device auf 60 Sekunden geändert, um die Überlegungen zu vereinfachen. Bei Event-Steuerung könnte man nicht sagen ob die letzten 10 Events in den letzten 5 Sekunden oder in den letzten 10 Minuten aufgetreten sind.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;power=xxx&#039;&#039;&#039;: Wurde teilweise etwas höher als der tatsächliche Wert gewählt, um nach dem Einschalten sicherzustellen, dass nicht sofort der gesamte Überschuss &amp;quot;verbraucht&amp;quot; wird und somit gleich wieder abgeschaltet wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;auto=xxx&#039;&#039;&#039;: Jeder Verbraucher benötigt sein eigenes auto-Reading, da die Automatik für die Regelung in verschiedenen Stufen ein- und ausgeschaltet werden muss.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;dum_valve&#039;&#039;&#039;: Dummy-Device in dem die Zustände der Regelung abgebildet werden. Notifys lesen bzw. befüllen diesen Dummy und steuern dann die Hardware bzw. wird der Dummy durch Werte aus der Hardware passend befüllt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;surpmeth=median_xx:&#039;&#039;&#039; Durch das asynchron=0 in allen Consumern und beim Generator wird von Solarforcast genau alle 60 Sekunden ein Zyklus ausgeführt. Das führt dazu, dass der Median genau über xx Minuten gebildet wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;locktime=xx:xx&#039;&#039;&#039;: Sicherheitsvorkehrung, dass nicht zu oft geschaltet werden kann. Schont die Hardware und verhindert ein Schwingen. Abhänging vom geschalteten Gerät. z.B. soll die Wärmepumpe möglichst wenig takten, um die Lebensdauer zu verlängern.&lt;br /&gt;
&lt;br /&gt;
Es gibt dann auch noch einige weitere Sicherheitsvorkehrungen, um diese Regelung möglichst stabil und hardwareschonend zu gestalten. Die Schwierigkeit sind nicht die Bilderbuchtage mit einem glatten Verlauf der PV-Leistung, sondern Tage mit ständig wechselndem Überschuss in der Nähe der Schaltgrenzen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;swoncond= dum_valve:sf_true:{main::xxx_On}:&#039;&#039;&#039; Hier werden verschiedene Zustände geprüft, um ein Gerät nur dann einzuschalten, wenn es sinnvoll und sicher ist.&lt;br /&gt;
&lt;br /&gt;
Details unter &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;swoffcond= dum_valve:sf_true:{main::xxx_Off}&#039;&#039;&#039;: Hier werden verschiedene Zustände geprüft, um ein Gerät nur dann auszuschalten, wenn es sinnvoll und sicher ist.&lt;br /&gt;
&lt;br /&gt;
Details unter &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pump Low: (consumer01)&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
dum_valve:Pool+Low&lt;br /&gt;
aliasshort=Low&lt;br /&gt;
asynchron=0&lt;br /&gt;
auto=pump_low_auto&lt;br /&gt;
icon=scene_pool&lt;br /&gt;
interruptable=0&lt;br /&gt;
mintime=SunPath:0:180&lt;br /&gt;
mode=must&lt;br /&gt;
noshow=0&lt;br /&gt;
notafter=08:00&lt;br /&gt;
off=&amp;quot;pump_low off&amp;quot;&lt;br /&gt;
on=&amp;quot;pump_low on&amp;quot;&lt;br /&gt;
pcurr=pump_low_power&lt;br /&gt;
power=250&lt;br /&gt;
surpmeth=median_13&lt;br /&gt;
switchdev=dum_valve&lt;br /&gt;
swoffcond=dum_valve:sf_true:{main::Check_Pump_Low_Off}&lt;br /&gt;
swoncond=dum_valve:sf_true:{main::Check_Pump_Low_On}&lt;br /&gt;
swstate=pump_low:on:off&lt;br /&gt;
type=other&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterungen:&lt;br /&gt;
&lt;br /&gt;
* Die Pumpe soll ab Sonnenaufgang, bis 180 Minuten nach Sonnenuntergang (mintime=SunPath:0:180), bei mindestens 250W Überschuss (power=250) gestartet werden.&lt;br /&gt;
* Die Pumpe muss spätestens um 8:00 eingeplant werden (notafter=08:00), um das Umwälzen auch bei Schlechtwetter zu garantieren.&lt;br /&gt;
* Die Pumpe muss gestartet werden (mode=must) sobald sie eingeplant ist.&lt;br /&gt;
* swoncond sorgt dafür, dass sie vor 8:00 nur bei genügend Überschuss gestartet wird, oder es 8:00 ist. swondcond ist mit der eigentlichen Einschaltbedingung (eingeplant und mode=must) UND verknüpft. -&amp;gt; siehe &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039;&lt;br /&gt;
* Überschuss wird auch in swoncond mit der gewählten Methode geprüft.&lt;br /&gt;
* Die Pumpe darf, wenn sie gestartet wurde, auch bei fehlendem Überschuss nicht mehr unterbrochen werden. (interruptable=0)&lt;br /&gt;
* Die Pumpe wird am Ende der Einplanung ausgeschaltet oder wenn swoffcond erfüllt ist. swoffcond wird true, wenn eine gewisse Wassermenge umgewälzt wurde oder die Sicherheitsbedingungen nicht (mehr) erfüllt sind. swoffcond ist mit der eigentlichen Ausschaltbedingung (Planung endet) ODER verknüpft. -&amp;gt; siehe &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039;&lt;br /&gt;
* {&#039;&#039;&#039;main::&#039;&#039;&#039;xxx} dient dazu, um die Funktion xxx in der 99_mySolarForecastUtils.pm aus Solarforecast erreichbar zu machen.&lt;br /&gt;
* dum_valve:sf_true ist ein Reading, das fix 1 ist und als Vergleichswert für den in Klammern folgenden Perlcode dient. Wenn dieses Reading und der Perlcode gleich sind (1==1), ist die swoncond bzw swoffcond erfüllt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pump High: (consumer03)&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
dum_valve:Pool+High&lt;br /&gt;
aliasshort=High&lt;br /&gt;
type=other power=1000 asynchron=0&lt;br /&gt;
auto=pump_high_auto&lt;br /&gt;
icon=scene_pool&lt;br /&gt;
pcurr=pump_high_power&lt;br /&gt;
switchdev=dum_valve&lt;br /&gt;
swstate=pump_high:on:off&lt;br /&gt;
mode=can&lt;br /&gt;
mintime=SunPath&lt;br /&gt;
on=&amp;quot;pump_high on&amp;quot;&lt;br /&gt;
off=&amp;quot;pump_high off&amp;quot;&lt;br /&gt;
surpmeth=median_5&lt;br /&gt;
noshow=0&lt;br /&gt;
locktime=180:300&lt;br /&gt;
interruptable=1&lt;br /&gt;
swoncond=dum_valve:sf_true:{main::Check_Pump_High_On}&lt;br /&gt;
swoffcond=dum_valve:sf_true:{main::Check_Pump_High_Off}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterungen:&lt;br /&gt;
&lt;br /&gt;
* Pump HIgh ist die selbe Pumpe, aber eine höhere Durchflussmenge&lt;br /&gt;
* Der Mehrverbrauch beträgt eigentlich 800W wird hier aber mit 1000W angegeben, um eine Hysterese von ca. 200W zu erreichen und damit ein schnelles Abschalten nach dem Einschalten zu verhindern.&lt;br /&gt;
* Diese Stufe wird nur bei genügend Überschuss aktiviert. (mode=can)&lt;br /&gt;
* Diese Stufe wird bei fehlendem Überschuss unterbrochen. (interruptable=1)&lt;br /&gt;
* Diese Stufe wird nur während Sonnenschein eingeplant. (mintime=SunPath)&lt;br /&gt;
* locktime sorgt dafür, dass bei schnell wechselnden Bedingungen, nicht ständig umgeschaltet wird. -&amp;gt; Schonung der Pumpe.&lt;br /&gt;
* swoncond und swoffcond -&amp;gt; siehe &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Heatpump: (consumer05)&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
Pool_Strom_Heizung:Poolheizung&lt;br /&gt;
aliasshort=WP&lt;br /&gt;
type=other power=1800 asynchron=0&lt;br /&gt;
icon=sani_heating_heatpump&lt;br /&gt;
auto=heatpump_auto&lt;br /&gt;
pcurr=Pool_Pin32_monotonic_count_PowerCurrent:W&lt;br /&gt;
etotal=Pool_Pin32_monotonic_count_EnergyMeter:kWh&lt;br /&gt;
switchdev=dum_valve&lt;br /&gt;
swstate=heatpump:on:off&lt;br /&gt;
mode=can&lt;br /&gt;
locktime=600:600&lt;br /&gt;
mintime=SunPath&lt;br /&gt;
on=&amp;quot;heatpump on&amp;quot;&lt;br /&gt;
off=&amp;quot;heatpump off&amp;quot;&lt;br /&gt;
surpmeth=median_13&lt;br /&gt;
noshow=0&lt;br /&gt;
swoncond=dum_valve:sf_true:{main::CheckWPOn}&lt;br /&gt;
interruptable=1&lt;br /&gt;
swoffcond=dum_valve:sf_true:{main::CheckWPOff}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterungen:&lt;br /&gt;
&lt;br /&gt;
* Der Verbrauch ist hier wieder etwas zu hoch angegeben (1800W statt realen 1500W), um eine Hysterese zu erreichen.&lt;br /&gt;
* Da es sich hier um eine Wärmepumpe handelt, die nicht getaktet werden sollte, ist die locktime in beide Schaltrichtungen mit 10 Minuten relativ hoch.&lt;br /&gt;
* Die Ermittlung des Medians ist mit 13 Minuten recht lang, um den Überschuss über lange Zeit zu glätten, damit möglichst selten geschaltet wird. Diese 13 Minuten in Kombination mit der Locktime und der Hysterese führten bei dieser Anlage bisher dazu, dass höchstens 5 Zyklen am Tag geschaltet wurden. -&amp;gt; Hier kann das individuelle Schaltverhalten optimiert werden. Besseres Verfolgen des Überschusses vs. weniger Taktungen der Wärmepumpe.&lt;br /&gt;
* swoncond und swoffcond -&amp;gt; siehe &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Nachbars Pool: (consumer07)&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
Shelly_Plug_S_1:Kurz+Pool&lt;br /&gt;
aliasshort=Kurz&lt;br /&gt;
type=other power=650 asynchron=0&lt;br /&gt;
icon=scene_pool&lt;br /&gt;
auto=pool_kurz_auto&lt;br /&gt;
pcurr=power:W&lt;br /&gt;
switchdev=dum_valve&lt;br /&gt;
swstate=pool_kurz:on:off&lt;br /&gt;
mode=can&lt;br /&gt;
mintime=SunPath&lt;br /&gt;
on=&amp;quot;pool_kurz on&amp;quot;&lt;br /&gt;
off=&amp;quot;pool_kurz off&amp;quot;&lt;br /&gt;
surpmeth=median_13&lt;br /&gt;
noshow=0&lt;br /&gt;
interruptable=1&lt;br /&gt;
locktime=600:300&lt;br /&gt;
swoncond=dum_valve:sf_true:{main::Check_Pool_Kurz_On}&lt;br /&gt;
swoffcond=dum_valve:sf_true:{main::Check_Pool_Kurz_Off}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterungen:&lt;br /&gt;
&lt;br /&gt;
* Dieser Consumer dient als Einspeisebegrenzungsschutz und wird aktiviert, sobald sich der Überschuss der Einspeisegrenze nähert.  -&amp;gt; siehe &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039;&lt;br /&gt;
* Verbraucher kann recht träge geschaltet werden. (surpmeth=median_13 und locktime=600:300)&lt;br /&gt;
* Consumer wird ausgeschaltet wenn ein gewisser Überschuss unterschritten wird und somit die Einspeisegrenze wieder weit genug weg ist. -&amp;gt; siehe &#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039; &lt;br /&gt;
&#039;&#039;&#039;99_mySolarForecastUtils.pm&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Hier sind die Funktionen, die für diese Regelung verwendet werden, der Übersichtlichkeit halber in einzelnen Blöcken mit Erklärungen dargestellt.&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
package main;&lt;br /&gt;
use strict;&lt;br /&gt;
use warnings;&lt;br /&gt;
&lt;br /&gt;
my $pool_dummy = &#039;dum_valve&#039;;&lt;br /&gt;
my $pool_temperature = &#039;Pool_Temp&#039;;&lt;br /&gt;
my $flow_sensor = &#039;Pool_Pin_A0&#039;;&lt;br /&gt;
my $water_counter_l1 = &#039;pump_l1_hc&#039;;&lt;br /&gt;
my $water_counter_l2 = &#039;pump_l2_hc&#039;;&lt;br /&gt;
my $target_temp = 28.4; # maximum temperature for heating, without infeed limit&lt;br /&gt;
my $pool_kurz_sw_hofb = &#039;PwSw_Schalter&#039;; # Homematic switch at pool neighbour&lt;br /&gt;
my $pool_kurz_sw_kurz = &#039;Shelly_Plug_S_1&#039;; # Shelly switch for pool Kurz at home&lt;br /&gt;
&lt;br /&gt;
my $pump_low_id			= sprintf &amp;quot;%02d&amp;quot;, 1;&lt;br /&gt;
my $pump_high_id		= sprintf &amp;quot;%02d&amp;quot;, 3;&lt;br /&gt;
my $heatpump_id			= sprintf &amp;quot;%02d&amp;quot;, 5;&lt;br /&gt;
my $pool_kurz_id		= sprintf &amp;quot;%02d&amp;quot;, 7;&lt;br /&gt;
&lt;br /&gt;
my $switch_delay = 160; # just under three minutes&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sub&lt;br /&gt;
SolarForecastUtils_Initialize($$)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Hier werden die IDs der Geräte definiert, um über sprechende Namen darauf zugreifen zu können.&lt;br /&gt;
* Einige Devices und Parameter, die immer wieder gebraucht werden sind hier angegeben.&lt;br /&gt;
* sprintf wird verwendet um sicherzustellen, dass die IDs zweistellig sind. z.B. 3 -&amp;gt; 03&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pump Low&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
###################&lt;br /&gt;
### Pumpe - LOW ###&lt;br /&gt;
###################&lt;br /&gt;
&lt;br /&gt;
sub&lt;br /&gt;
Check_Pump_Low_On{&lt;br /&gt;
my $name = &#039;energy_mgmt&#039;;&lt;br /&gt;
my $surplus	= FHEM::SolarForecast::ConsumerVal ($name,$pump_low_id,&#039;surpmethResult&#039;,&#039;&#039;);&lt;br /&gt;
my $nom_power	= FHEM::SolarForecast::ConsumerVal ($name,$pump_low_id,&#039;power&#039;,&#039;&#039;);&lt;br /&gt;
my ($min, $hour) = (localtime())[1,2];&lt;br /&gt;
my $timer = 0;&lt;br /&gt;
if($hour &amp;gt; 8 || ($hour == 8 &amp;amp;&amp;amp; $min &amp;gt;= 0)){$timer=1;}&lt;br /&gt;
&lt;br /&gt;
if(ReadingsVal($pool_dummy,&amp;quot;valve_position&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;normal&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&amp;quot;special_function&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;none&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; ($surplus &amp;gt; $nom_power&lt;br /&gt;
		|| $timer&lt;br /&gt;
	)&lt;br /&gt;
)&lt;br /&gt;
	{&lt;br /&gt;
	return 1;}&lt;br /&gt;
else&lt;br /&gt;
	{&lt;br /&gt;
	return 0;}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub&lt;br /&gt;
Check_Pump_Low_Off{&lt;br /&gt;
my $bath_mode = ReadingsVal($pool_dummy,&amp;quot;bath_mode&amp;quot;,0);&lt;br /&gt;
my $m3_l1 = ReadingsVal($water_counter_l1,&amp;quot;waterAmount&amp;quot;,0);&lt;br /&gt;
my $m3_l2 = ReadingsVal($water_counter_l2,&amp;quot;waterAmount&amp;quot;,0);&lt;br /&gt;
my $water_volume = AttrVal($pool_dummy,&amp;quot;water_volume&amp;quot;,42); #42m³&lt;br /&gt;
my $desired_cf_l1 = AttrVal($pool_dummy,&amp;quot;desired_cf_l1&amp;quot;,0); #circulation_factor_level1&lt;br /&gt;
my $desired_cf_l2 = AttrVal($pool_dummy,&amp;quot;desired_cf_l2&amp;quot;,0); #circulation_factor_level2&lt;br /&gt;
my $bathmode_factor = AttrVal($pool_dummy,&amp;quot;bathmode_factor&amp;quot;,1);&lt;br /&gt;
my $desired_amount_l1;&lt;br /&gt;
my $desired_amount_l2;&lt;br /&gt;
my $amount = $m3_l1 + $m3_l2;&lt;br /&gt;
if($bath_mode == 1){&lt;br /&gt;
	$desired_amount_l1 = $desired_cf_l1 * $water_volume;&lt;br /&gt;
	$desired_amount_l2 = $desired_cf_l2 * $water_volume;&lt;br /&gt;
}else{&lt;br /&gt;
	$desired_amount_l1 = $desired_cf_l1 * $water_volume/$bathmode_factor;&lt;br /&gt;
	$desired_amount_l2 = $desired_cf_l2 * $water_volume/$bathmode_factor;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if(($amount &amp;gt; $desired_amount_l1)&lt;br /&gt;
	&amp;amp;&amp;amp; (ReadingsVal($pool_dummy,&amp;quot;pump_high&amp;quot;,&amp;quot;on&amp;quot;) eq &amp;quot;off&amp;quot;  &amp;amp;&amp;amp; ReadingsAge($pool_dummy,&amp;quot;pump_high&amp;quot;,0) &amp;gt;160)&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&amp;quot;special_function&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;none&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&amp;quot;valve_position&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;normal&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
	{&lt;br /&gt;
		return 1;&lt;br /&gt;
	}else{&lt;br /&gt;
		return 0;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Einschalten:&lt;br /&gt;
&lt;br /&gt;
* Es wird der errechnete Wert für den Überschuss von SolarForecast verwendet und auch der benötigte Überschuss aus dem Consumer ausgelesen.&lt;br /&gt;
* Das Einschalten wird erst durch erfolgreiche Prüfung der Hardwarevoraussetzungen erlaubt. -&amp;gt; Das Motorventil steht richtig und es ist kein Sonderbetrieb (Reinigung, Massage, Winter, o.ä.) aktiv.&lt;br /&gt;
* Es muss entweder genügend Überschuss vorhanden sein oder es ist 8:00.&lt;br /&gt;
&lt;br /&gt;
Ausschalten:&lt;br /&gt;
&lt;br /&gt;
* Wenn der Badebetrieb aktiv ist (bath_mode==1, [bei Außentemperatur &amp;gt; 24°C]) wird nach 90m³ Gesamtumwälzung ausgeschaltet.&lt;br /&gt;
* Wenn der Badebetrieb nicht aktiv ist (bath_mode==0, [bei Außentemperatur &amp;lt; 20°C]) wird bereits nach 42m³ Gesamtumwälzung ausgeschaltet.&lt;br /&gt;
* Prüfung, ob Pump High schon aus ist. Sollte durch die Logik gar nicht möglich sein, aber es könnte manuell oder durch einen (Logik-)Fehler trotzdem eingeschaltet sein.&lt;br /&gt;
* Prüfung, ob alle Hardware-Funktionen in einem für die Automatik geeigneten Zustand sind.&lt;br /&gt;
* Es wird auch geprüft, ob Pump High seit mindestens 3 Minuten aus ist, um zu verhindern, dass wegen fehlendem Überschuss gleich mehrere Geräte ausgeschaltet werden.&lt;br /&gt;
* Da die Zykluszeit 60 Sekunden ist, führen die 160 Sekunden dazu, dass nach drei Minuten(180 Sekunden) ein definierter Wert gilt. Mit 180 Sekunden könnte es manchaml eine Minute mehr sein.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pump High&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
####################&lt;br /&gt;
### Pumpe - HIGH ###&lt;br /&gt;
####################&lt;br /&gt;
&lt;br /&gt;
sub&lt;br /&gt;
Check_Pump_High_On{&lt;br /&gt;
my $bath_mode = ReadingsVal($pool_dummy,&amp;quot;bath_mode&amp;quot;,0);&lt;br /&gt;
my $m3_l1 = ReadingsVal($water_counter_l1,&amp;quot;waterAmount&amp;quot;,0);&lt;br /&gt;
my $m3_l2 = ReadingsVal($water_counter_l2,&amp;quot;waterAmount&amp;quot;,0);&lt;br /&gt;
my $water_volume = AttrVal($pool_dummy,&amp;quot;water_volume&amp;quot;,42); #42m³&lt;br /&gt;
my $desired_cf_l1 = AttrVal($pool_dummy,&amp;quot;desired_cf_l1&amp;quot;,0); #circulation_factor_level1&lt;br /&gt;
my $desired_cf_l2 = AttrVal($pool_dummy,&amp;quot;desired_cf_l2&amp;quot;,0); #circulation_factor_level2&lt;br /&gt;
my $bathmode_factor = AttrVal($pool_dummy,&amp;quot;bathmode_factor&amp;quot;,1);&lt;br /&gt;
my $desired_amount_l1;&lt;br /&gt;
my $desired_amount_l2;&lt;br /&gt;
my $amount = $m3_l1 + $m3_l2;&lt;br /&gt;
if($bath_mode == 1){&lt;br /&gt;
	$desired_amount_l1 = $desired_cf_l1 * $water_volume;&lt;br /&gt;
	$desired_amount_l2 = $desired_cf_l2 * $water_volume;&lt;br /&gt;
}else{&lt;br /&gt;
	$desired_amount_l1 = $desired_cf_l1 * $water_volume/$bathmode_factor;&lt;br /&gt;
	$desired_amount_l2 = $desired_cf_l2 * $water_volume/$bathmode_factor;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if(($amount &amp;lt; $desired_amount_l2)&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&amp;quot;special_function&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;none&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&amp;quot;valve_position&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;normal&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; (ReadingsVal($pool_dummy,&amp;quot;pump_low&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot;  &amp;amp;&amp;amp; ReadingsAge($pool_dummy,&amp;quot;pump_low&amp;quot;,0) &amp;gt; $switch_delay)&lt;br /&gt;
)&lt;br /&gt;
	{return 1}&lt;br /&gt;
else&lt;br /&gt;
	{return 0}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sub&lt;br /&gt;
Check_Pump_High_Off{&lt;br /&gt;
my $bath_mode = ReadingsVal($pool_dummy,&amp;quot;bath_mode&amp;quot;,0);&lt;br /&gt;
my $m3_l1 = ReadingsVal($water_counter_l1,&amp;quot;waterAmount&amp;quot;,0);&lt;br /&gt;
my $m3_l2 = ReadingsVal($water_counter_l2,&amp;quot;waterAmount&amp;quot;,0);&lt;br /&gt;
my $water_volume = AttrVal($pool_dummy,&amp;quot;water_volume&amp;quot;,42); #42m³&lt;br /&gt;
my $desired_cf_l1 = AttrVal($pool_dummy,&amp;quot;desired_cf_l1&amp;quot;,0); #circulation_factor_level1&lt;br /&gt;
my $desired_cf_l2 = AttrVal($pool_dummy,&amp;quot;desired_cf_l2&amp;quot;,0); #circulation_factor_level2&lt;br /&gt;
my $bathmode_factor = AttrVal($pool_dummy,&amp;quot;bathmode_factor&amp;quot;,1);&lt;br /&gt;
my $desired_amount_l1;&lt;br /&gt;
my $desired_amount_l2;&lt;br /&gt;
my $amount = $m3_l1 + $m3_l2;&lt;br /&gt;
&lt;br /&gt;
if($bath_mode == 1){&lt;br /&gt;
	$desired_amount_l1 = $desired_cf_l1 * $water_volume;&lt;br /&gt;
	$desired_amount_l2 = $desired_cf_l2 * $water_volume;&lt;br /&gt;
}else{&lt;br /&gt;
	$desired_amount_l1 = $desired_cf_l1 * $water_volume/$bathmode_factor;&lt;br /&gt;
	$desired_amount_l2 = $desired_cf_l2 * $water_volume/$bathmode_factor;&lt;br /&gt;
}&lt;br /&gt;
if(($amount &amp;gt; $desired_amount_l2)&lt;br /&gt;
	&amp;amp;&amp;amp; (ReadingsVal($pool_dummy,&amp;quot;heatpump&amp;quot;,&amp;quot;on&amp;quot;) eq &amp;quot;off&amp;quot;  &amp;amp;&amp;amp; ReadingsAge($pool_dummy,&amp;quot;heatpump&amp;quot;,0) &amp;gt;160)&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&amp;quot;special_function&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;none&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&amp;quot;valve_position&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;normal&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
	{return 1}&lt;br /&gt;
else&lt;br /&gt;
	{return 0}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Einschalten:&lt;br /&gt;
&lt;br /&gt;
* Prüfung, ob die gewünschte Umwälzung bezüglich des Badebetriebs noch nicht erreicht wurde.&lt;br /&gt;
* Prüfung der Hardwarevoraussetzungen.&lt;br /&gt;
* Prüfung, ob Pump Low schon seit mindestens 3 Minuten aktiv ist. (sollte eigentlich immer der Fall sein, wenn die Automatik für Pump High aktiv ist)&lt;br /&gt;
&lt;br /&gt;
Ausschalten:&lt;br /&gt;
&lt;br /&gt;
* Prüfung, ob die gewünschte Umwälzung bezüglich des Badebetriebs bereits erreicht wurde.&lt;br /&gt;
* Prüfung der Hardwarevoraussetzungen.&lt;br /&gt;
* Prüfung, ob die Wärmepumpe schon seit mindestens 3 Minuten aus ist. Ermöglicht drei Minuten lang, den Median für den Überschuss zu aktualisieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wärmepumpe&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
##################&lt;br /&gt;
### Wärempumpe ###&lt;br /&gt;
##################&lt;br /&gt;
sub&lt;br /&gt;
CheckWPOn(){&lt;br /&gt;
my $name = &#039;energy_mgmt&#039;;&lt;br /&gt;
my $surplus_on = 5000; # minimum surplus to avoid infeed limit with heatpump&lt;br /&gt;
my $surplus_restart = 3000; # minimum surplus to re-enable heatpump, when avoidung infeed limit and a big consumer turned the hp off&lt;br /&gt;
&lt;br /&gt;
my $planstate = FHEM::SolarForecast::ConsumerVal ($name,$heatpump_id,&#039;planstate&#039;,&#039;&#039;);&lt;br /&gt;
my $surplus	= FHEM::SolarForecast::ConsumerVal ($name,$heatpump_id,&#039;surpmethResult&#039;,&#039;&#039;);&lt;br /&gt;
if ($planstate =~ /^([^:]+)/) {&lt;br /&gt;
	$planstate = $1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
if(ReadingsVal($flow_sensor,&amp;quot;reading&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; (ReadingsVal($pool_dummy,&amp;quot;special_function&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;none&amp;quot;&lt;br /&gt;
		|| ReadingsVal($pool_dummy,&amp;quot;special_function&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;party&amp;quot;&lt;br /&gt;
	)&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&#039;heat_protection&#039;,&#039;on&#039;) eq &amp;quot;off&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; ReadingsVal($pool_dummy,&amp;quot;valve_position&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;normal&amp;quot;&lt;br /&gt;
	&amp;amp;&amp;amp; (ReadingsVal($pool_dummy,&amp;quot;pump_high&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot;  &amp;amp;&amp;amp; ReadingsAge($pool_dummy,&amp;quot;pump_high&amp;quot;,0) &amp;gt; $switch_delay)&lt;br /&gt;
	&amp;amp;&amp;amp; (ReadingsVal($pool_temperature,&amp;quot;temperature&amp;quot;,30) &amp;lt; $target_temp &lt;br /&gt;
		|| (ReadingsVal($pool_temperature,&amp;quot;temperature&amp;quot;,15) &amp;gt;= $target_temp&lt;br /&gt;
			&amp;amp;&amp;amp; ( $surplus &amp;gt; $surplus_on&lt;br /&gt;
				|| (($planstate eq &amp;quot;replanned&amp;quot;) &amp;amp;&amp;amp; ($surplus &amp;gt; $surplus_restart))&lt;br /&gt;
			)&lt;br /&gt;
		)&lt;br /&gt;
	)&lt;br /&gt;
)&lt;br /&gt;
	{return 1}&lt;br /&gt;
else&lt;br /&gt;
	{return 0}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub&lt;br /&gt;
CheckWPOff{&lt;br /&gt;
my $name = &#039;energy_mgmt&#039;;&lt;br /&gt;
my $surplus_off = 1000; #minimum surplus to keep heatpump on, when avoiding infeed limit&lt;br /&gt;
&lt;br /&gt;
my $surplus	= FHEM::SolarForecast::ConsumerVal ($name,$heatpump_id,&#039;surpmethResult&#039;,&#039;&#039;);&lt;br /&gt;
if((ReadingsVal($flow_sensor,&amp;quot;reading&amp;quot;,&amp;quot;on&amp;quot;) eq &amp;quot;off&amp;quot;&lt;br /&gt;
	|| !((ReadingsVal($pool_dummy,&amp;quot;special_function&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;none&amp;quot;) &lt;br /&gt;
		|| (ReadingsVal($pool_dummy,&amp;quot;special_function&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;party&amp;quot;)&lt;br /&gt;
	)&lt;br /&gt;
	|| ReadingsVal($pool_dummy,&#039;heat_protection&#039;,&#039;off&#039;) eq &amp;quot;on&amp;quot;&lt;br /&gt;
	|| ReadingsVal($pool_dummy,&amp;quot;valve_position&amp;quot;,&amp;quot;&amp;quot;) ne &amp;quot;normal&amp;quot;&lt;br /&gt;
	|| ((ReadingsVal($pool_temperature,&amp;quot;temperature&amp;quot;,15) &amp;gt; $target_temp)&lt;br /&gt;
		&amp;amp;&amp;amp; ($surplus &amp;lt; $surplus_off)&lt;br /&gt;
		)&lt;br /&gt;
	)&lt;br /&gt;
	&amp;amp;&amp;amp; (ReadingsVal($pool_dummy,&amp;quot;heatpump&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot;  &amp;amp;&amp;amp; ReadingsAge($pool_dummy,&amp;quot;heatpump&amp;quot;,0) &amp;gt; $switch_delay)&lt;br /&gt;
)&lt;br /&gt;
	{return 1}&lt;br /&gt;
else&lt;br /&gt;
	{return 0}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Einschalten:&lt;br /&gt;
&lt;br /&gt;
* Prüfung, ob Flussensor ein. (Pool_pin_A0) -&amp;gt; Wasser fließt durch die Wärmepumpe.&lt;br /&gt;
* Prüfung, ob Pool in Normal- oder Partybetrieb ist und das Ventil in Normalposition ist. In beiden Fällen läuft Pump High.&lt;br /&gt;
* Einschalten wenn genug Überschuss da ist und die Temperatur &amp;lt;28.4°C ist oder wenn über 5000W Überschuss (Einspeisebegrenzung bei 5900W) da ist, egal wie warm der Pool ist.&lt;br /&gt;
* Sonderfall: Wenn die Wärmepumpe durch einen/mehrere Großverbrucher &amp;quot;kurz&amp;quot; ausgeschaltet wurde (swoffcond) wird sie ausgeplant. Ein Notify plant sie wieder ein. Wenn das passiert, soll die Wärmepumpe aber wieder bei 3000W Überschuss gestartet werden, um die Energie noch weiter zu nutzen bis der Überschuss unter 1000W fällt. (Hysterese von 500W, da die WP nur 1500W braucht)&lt;br /&gt;
&lt;br /&gt;
Ausschalten:&lt;br /&gt;
&lt;br /&gt;
* Ausschalten sobald hardwaremäßig irgendetwas faul ist. (Betriebsart, Ventilstellung, Durchfluss)&lt;br /&gt;
* Ausschalten wenn kein Überschuss. (interruptable=1)&lt;br /&gt;
* Wenn Wassertemperatur über 28,5°C und der Überschuss kleiner als 1000W ist.&lt;br /&gt;
* Nur schalten, wenn Wärmepumpe läuft und seit mindestens 3 Minuten läuft (Sicherheit). Das verhindert das erneute Triggern des Notifys, wenn die Pumpe schon aus ist. Sollte nicht passieren, aber sicher ist sicher.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Nachbar Pool&#039;&#039;&#039;&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
###################&lt;br /&gt;
### Pool - KURZ ###&lt;br /&gt;
###################&lt;br /&gt;
sub&lt;br /&gt;
Check_Pool_Kurz_On{&lt;br /&gt;
my $name = &#039;energy_mgmt&#039;;&lt;br /&gt;
my $surplus	= FHEM::SolarForecast::ConsumerVal ($name,$pool_kurz_id,&#039;surpmethResult&#039;,&#039;&#039;);&lt;br /&gt;
if(($surplus &amp;gt; 3000)&lt;br /&gt;
	&amp;amp;&amp;amp; ((ReadingsVal($pool_dummy,&amp;quot;heatpump&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot;  &amp;amp;&amp;amp; ReadingsAge($pool_dummy,&amp;quot;heatpump&amp;quot;,0) &amp;gt;160) || ReadingsVal($pool_dummy,&#039;heat_protection&#039;,&#039;off&#039;) eq &#039;on&#039; ))&lt;br /&gt;
	{return 1}&lt;br /&gt;
else&lt;br /&gt;
	{return 0}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub&lt;br /&gt;
Check_Pool_Kurz_Off{&lt;br /&gt;
my $name = &#039;energy_mgmt&#039;;&lt;br /&gt;
my $surplus	= FHEM::SolarForecast::ConsumerVal ($name,$pool_kurz_id,&#039;surpmethResult&#039;,&#039;&#039;);&lt;br /&gt;
if(($surplus &amp;lt; 1000 )&lt;br /&gt;
	&amp;amp;&amp;amp; (ReadingsVal($pool_dummy,&amp;quot;pool_kurz&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot;  &amp;amp;&amp;amp; ReadingsAge($pool_dummy,&amp;quot;pool_kurz&amp;quot;,0) &amp;gt;160))&lt;br /&gt;
	{return 1}&lt;br /&gt;
else&lt;br /&gt;
	{return 0}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Einschalten:&lt;br /&gt;
&lt;br /&gt;
* Überschuss ist nahe der Einspeisegrenze (5900W) und Wärmepumpe läuft seit mindestens 3 Minuten. Es wird schon bei 3000W eingeschaltet, um dem Nachbarn etwas Gutes zu tun ;-)&lt;br /&gt;
&lt;br /&gt;
Ausschalten:&lt;br /&gt;
&lt;br /&gt;
* Überschuss ist unter 1000W. -&amp;gt; Um noch Reserven für den eigenen Verbrauch zu haben.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hilfsfunktionen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ctrlUserExitFn&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Hier erfolgt nach jedem Zyklus die Festlegung der verschiedenen Automatikmodi.&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
#############################################################&lt;br /&gt;
##################### Exit Function #########################&lt;br /&gt;
#############################################################&lt;br /&gt;
my $ctrl_dummy	= &amp;quot;dum_valve&amp;quot;; # dummy device for pool-control&lt;br /&gt;
my $automatic	= ReadingsNum($ctrl_dummy,&amp;quot;sf_automatic&amp;quot;,1);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if($automatic){&lt;br /&gt;
&lt;br /&gt;
	my $pump_low_state = ReadingsVal($ctrl_dummy,&#039;pump_low&#039;,&#039;on&#039;);&lt;br /&gt;
	my $pump_low_auto	= FHEM::SolarForecast::ConsumerVal ($name, $pump_low_id, &#039;auto&#039;, &#039;&#039;);	# Automatikmodus der Pumpe&lt;br /&gt;
&lt;br /&gt;
	my $pump_high_state = ReadingsVal($ctrl_dummy,&#039;pump_high&#039;,&#039;on&#039;);&lt;br /&gt;
	my $pump_high_auto	= FHEM::SolarForecast::ConsumerVal ($name, $pump_high_id, &#039;auto&#039;, &#039;&#039;);	# Automatikmodus der Pumpe&lt;br /&gt;
&lt;br /&gt;
	my $heatpump_state = ReadingsVal($ctrl_dummy,&#039;heatpump&#039;,&#039;on&#039;);&lt;br /&gt;
	my $heatpump_auto	= FHEM::SolarForecast::ConsumerVal ($name, $heatpump_id, &#039;auto&#039;, &#039;&#039;);	# Automatikmodus der Wärmepumpe&lt;br /&gt;
	my $pool_heat_protection = ReadingsVal($ctrl_dummy,&#039;heat_protection&#039;,&#039;on&#039;);&lt;br /&gt;
&lt;br /&gt;
	my $pool_kurz_state = ReadingsVal($ctrl_dummy,&#039;pool_kurz&#039;,&#039;on&#039;);&lt;br /&gt;
	my $pool_kurz_auto	= FHEM::SolarForecast::ConsumerVal ($name, $pool_kurz_id, &#039;auto&#039;, &#039;&#039;);	# Automatikmodus des Pools&lt;br /&gt;
&lt;br /&gt;
	my $pump_low_delay	= FHEM::SolarForecast::ConsumerVal ($name, $pump_low_id, &#039;surpmeth&#039;, &#039;&#039;);&lt;br /&gt;
	if ($pump_low_delay =~ /_(\d+)/) {&lt;br /&gt;
    	$pump_low_delay = int(($1/2 + 1) * 60);&lt;br /&gt;
		Log3 (&amp;quot;SolarForecast&amp;quot;, 1, &amp;quot;SolarForecast: pump_low_delay: $pump_low_delay&amp;quot;);&lt;br /&gt;
	} else {&lt;br /&gt;
    	$pump_low_delay = 220;&lt;br /&gt;
		Log3 (&amp;quot;SolarForecast&amp;quot;, 1, &amp;quot;SolarForecast: warning - surpmeth for pump_low is: $pump_low_delay, but must be given as method_number, using fallback value&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	my $pump_high_delay	= FHEM::SolarForecast::ConsumerVal ($name, $pump_high_id, &#039;surpmeth&#039;, &#039;&#039;);&lt;br /&gt;
	if ($pump_high_delay =~ /_(\d+)/) {&lt;br /&gt;
    	$pump_high_delay = int(($1/2 + 1) * 60);&lt;br /&gt;
	} else {&lt;br /&gt;
    	$pump_high_delay = 160;&lt;br /&gt;
		Log3 (&amp;quot;SolarForecast&amp;quot;, 1, &amp;quot;SolarForecast: warning - surpmeth for pump_high is: $pump_high_delay, but must be given as method_number, using fallback value&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	my $heatpump_delay	= FHEM::SolarForecast::ConsumerVal ($name, $heatpump_id, &#039;surpmeth&#039;, &#039;&#039;);&lt;br /&gt;
	if ($heatpump_delay =~ /_(\d+)/) {&lt;br /&gt;
    	$heatpump_delay = int(($1/2 + 1) * 60);&lt;br /&gt;
	} else {&lt;br /&gt;
    	$heatpump_delay = 290;&lt;br /&gt;
		Log3 (&amp;quot;SolarForecast&amp;quot;, 1, &amp;quot;SolarForecast: warning - surpmeth for heatpump is: $heatpump_delay, but must be given as method_number, using fallback value&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	my $pool_kurz_delay	= FHEM::SolarForecast::ConsumerVal ($name, $pool_kurz_id, &#039;surpmeth&#039;, &#039;&#039;);&lt;br /&gt;
	if ($pool_kurz_delay =~ /_(\d+)/) {&lt;br /&gt;
    	$pool_kurz_delay = int(($1/2 + 1) * 60);&lt;br /&gt;
	} else {&lt;br /&gt;
    	$pool_kurz_delay = 160;&lt;br /&gt;
		Log3 (&amp;quot;SolarForecast&amp;quot;, 1, &amp;quot;SolarForecast: warning - surpmeth for pool_kurz is: $pool_kurz_delay, but must be given as method_number&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
############################&lt;br /&gt;
# Überhitzungsschutz aktiv #&lt;br /&gt;
############################&lt;br /&gt;
&lt;br /&gt;
if($pool_heat_protection eq &#039;on&#039;){&lt;br /&gt;
	if($heatpump_auto == 1){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy heatpump_auto 0&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
	if($heatpump_state eq &amp;quot;on&amp;quot;){&lt;br /&gt;
			fhem(&amp;quot;set $ctrl_dummy heatpump off&amp;quot;)&lt;br /&gt;
	}&lt;br /&gt;
	#################&lt;br /&gt;
	#### on-chain ###&lt;br /&gt;
	#################&lt;br /&gt;
	if($pump_low_state eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;pump_low&#039;,0) &amp;gt; $pump_high_delay &amp;amp;&amp;amp; $pump_high_auto == 0  &amp;amp;&amp;amp; $pool_kurz_auto == 0){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pump_high_auto 1&amp;quot;);&lt;br /&gt;
	}elsif($pump_high_state eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;pump_high&#039;,0) &amp;gt; $pool_kurz_delay &amp;amp;&amp;amp; $pool_kurz_auto == 0 &amp;amp;&amp;amp; $pump_high_auto == 1){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pool_kurz_auto 1&amp;quot;);&lt;br /&gt;
	}elsif($pool_kurz_state eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; $pool_kurz_auto == 1 &amp;amp;&amp;amp; $pump_high_auto == 1){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pump_high_auto 0&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	#################&lt;br /&gt;
	### off-chain ###&lt;br /&gt;
	#################&lt;br /&gt;
	if($pool_kurz_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;pool_kurz&#039;,0) &amp;gt; $pump_high_delay &amp;amp;&amp;amp; $pool_kurz_auto == 1 &amp;amp;&amp;amp; $pump_high_auto == 0){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pump_high_auto 1&amp;quot;);&lt;br /&gt;
	}elsif($pump_high_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $pump_high_auto == 1 &amp;amp;&amp;amp; $pool_kurz_auto == 1){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pool_kurz_auto 0&amp;quot;);&lt;br /&gt;
	}elsif($pump_low_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $pump_high_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $pump_high_auto == 1 &amp;amp;&amp;amp; $pool_kurz_auto == 0){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pump_high_auto 0&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
##################################&lt;br /&gt;
# Überhitzungsschutz nicht aktiv #&lt;br /&gt;
##################################&lt;br /&gt;
}else{&lt;br /&gt;
	if($heatpump_auto == 0 &amp;amp;&amp;amp; $heatpump_state eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		if($pool_kurz_state eq &amp;quot;on&amp;quot;){&lt;br /&gt;
			fhem(&amp;quot;set $ctrl_dummy pool_kurz off&amp;quot;);&lt;br /&gt;
		}	&lt;br /&gt;
		if($pool_kurz_auto == 1){&lt;br /&gt;
			fhem(&amp;quot;set $ctrl_dummy pool_kurz_auto 0&amp;quot;);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	#################&lt;br /&gt;
	#### on-chain ###&lt;br /&gt;
	#################&lt;br /&gt;
	if($pump_low_state eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;pump_low&#039;,0) &amp;gt; $pump_high_delay &amp;amp;&amp;amp; $pump_high_auto == 0 &amp;amp;&amp;amp; $heatpump_auto == 0 &amp;amp;&amp;amp; $pool_kurz_auto == 0){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pump_high_auto 1&amp;quot;);&lt;br /&gt;
	}elsif($pump_high_state eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;pump_high&#039;,0) &amp;gt; $heatpump_delay &amp;amp;&amp;amp; $heatpump_auto == 0 &amp;amp;&amp;amp; $pool_kurz_auto == 0 &amp;amp;&amp;amp; $pump_high_auto == 1){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy heatpump_auto 1&amp;quot;);&lt;br /&gt;
	}elsif($heatpump_state eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;heatpump&#039;,0) &amp;gt; $pool_kurz_delay &amp;amp;&amp;amp; $pump_high_state eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; $pool_kurz_auto == 0 &amp;amp;&amp;amp; $pump_high_auto == 1 &amp;amp;&amp;amp; $heatpump_auto == 1){&lt;br /&gt;
		# pump_high muss on sein, da sonst die automatik ausgeschaltet wird wenn die pumpe gerade interrupted ist.&lt;br /&gt;
		# Bei unterschiedlichen Medianlängen ist es möglich, dass WP ein ist und pump_high abschaltet wegen surplus == 0&lt;br /&gt;
		# Nachbarpool würde auf EIN warten, aber pump_high dürfte nicht wieder einschalten, obwohl genügend Überschuss da ist.&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pump_high_auto 0&amp;quot;);&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pool_kurz_auto 1&amp;quot;);&lt;br /&gt;
	}elsif($pool_kurz_state eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; $pool_kurz_auto == 1 &amp;amp;&amp;amp; $heatpump_auto == 1 &amp;amp;&amp;amp; $pump_high_auto == 0){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy heatpump_auto 0&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	#################&lt;br /&gt;
	### off-chain ###&lt;br /&gt;
	#################&lt;br /&gt;
	if($pool_kurz_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;pool_kurz&#039;,0) &amp;gt; $heatpump_delay &amp;amp;&amp;amp; $pool_kurz_auto == 1 &amp;amp;&amp;amp; $heatpump_auto == 0 &amp;amp;&amp;amp; $pump_high_auto == 0){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy heatpump_auto 1&amp;quot;);&lt;br /&gt;
	}elsif($heatpump_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;heatpump&#039;,0) &amp;gt; $pump_high_delay &amp;amp;&amp;amp; $heatpump_auto == 1 &amp;amp;&amp;amp; $pump_high_auto == 0 &amp;amp;&amp;amp; $pool_kurz_auto == 1){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pool_kurz_auto 0&amp;quot;);&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pump_high_auto 1&amp;quot;);&lt;br /&gt;
	}elsif($pump_high_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $heatpump_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $pump_high_auto == 1 &amp;amp;&amp;amp; $heatpump_auto == 1 &amp;amp;&amp;amp; $pool_kurz_auto == 0){&lt;br /&gt;
#	heatpump muss aus sein, da sonst vor Ablauf der Locktime die Automatik ausgeschaltet wird und die WP nicht mehr ausschalten kann.&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy heatpump_auto 0&amp;quot;);&lt;br /&gt;
	}elsif($pump_low_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $pump_high_state eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $pump_high_auto == 1 &amp;amp;&amp;amp; $heatpump_auto == 0){&lt;br /&gt;
		fhem(&amp;quot;set $ctrl_dummy pump_high_auto 0&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
}&lt;br /&gt;
#################################################&lt;br /&gt;
# Check hardware-state vs. reading-state&lt;br /&gt;
# re-trigger notify&lt;br /&gt;
#################################################&lt;br /&gt;
my $delay_retrigger = 100; #delay time to wait until re-trigger the notify&lt;br /&gt;
&lt;br /&gt;
if(ReadingsVal($ctrl_dummy,&#039;pool_kurz&#039;,&#039;on&#039;) eq &#039;off&#039; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;pool_kurz&#039;,0) &amp;gt; $delay_retrigger &amp;amp;&amp;amp; ReadingsVal($pool_kurz_sw_kurz,&#039;state&#039;,&#039;off&#039;) eq &#039;on&#039;){&lt;br /&gt;
	# re-trigger notify&lt;br /&gt;
	fhem(&amp;quot;trigger dum_valve pool_kurz: off&amp;quot;);&lt;br /&gt;
	Log3 (&amp;quot;SolarForecast&amp;quot;, 1, &amp;quot;SolarForecast: Pool-Kurz-off must be re-triggered&amp;quot;);&lt;br /&gt;
	DebianMail(&#039;clemens@familie-hofbauer.at&#039;,&#039;Notify Pool Kurz&#039;,&amp;quot;Notify hat nicht ausgeschaltet -&amp;gt; re-trigger&amp;quot;);&lt;br /&gt;
}elsif(ReadingsVal($ctrl_dummy,&#039;pool_kurz&#039;,&#039;off&#039;) eq &#039;on&#039; &amp;amp;&amp;amp; ReadingsAge($ctrl_dummy,&#039;pool_kurz&#039;,0) &amp;gt; $delay_retrigger &amp;amp;&amp;amp; ReadingsVal($pool_kurz_sw_kurz,&#039;state&#039;,&#039;on&#039;) eq &#039;off&#039;){&lt;br /&gt;
	# re-trigger notify&lt;br /&gt;
	fhem(&amp;quot;trigger dum_valve pool_kurz: on&amp;quot;);&lt;br /&gt;
	Log3 (&amp;quot;SolarForecast&amp;quot;, 1, &amp;quot;SolarForecast: Pool-Kurz-on must be re-triggered&amp;quot;);&lt;br /&gt;
	DebianMail(&#039;clemens@familie-hofbauer.at&#039;,&#039;Notify Pool Kurz&#039;,&amp;quot;Notify hat nicht eingeschaltet -&amp;gt; re-trigger&amp;quot;);&lt;br /&gt;
}elsif((ReadingsVal($pool_kurz_sw_kurz,&#039;state&#039;,&#039;off&#039;) eq &#039;set_on&#039; || ReadingsVal($pool_kurz_sw_kurz,&#039;state&#039;,&#039;off&#039;) eq &#039;set_off&#039;) &amp;amp;&amp;amp; ReadingsAge($pool_kurz_sw_kurz,&#039;state&#039;,0) &amp;gt; $delay_retrigger){&lt;br /&gt;
	# state not clear&lt;br /&gt;
	Log3 (&amp;quot;SolarForecast&amp;quot;, 1, &amp;quot;SolarForecast: Pool-Kurz state unclear&amp;quot;);&lt;br /&gt;
	DebianMail(&#039;clemens@familie-hofbauer.at&#039;,&#039;Status Pool Kurz unklar&#039;,&amp;quot;Status HM-Schalter set_xx&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Erläuterungen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Verzögerungen der einzelnen Stufen:&#039;&#039;&#039;&lt;br /&gt;
** Die delays der einzelnen Stufen werden aus den surpmethods berechnet. Das dient dazu, dass der Median mit den Werten nach dem ein-/ausschalten des vorheriegen Verbrauchers bis zur Hälfte + einen Wert gefüllt wird und somit sicher das Umschalten des vorherigen Verbrauchers den Median nicht mehr beeinflusst.&lt;br /&gt;
* &#039;&#039;&#039;Überhitzungsschutz:&#039;&#039;&#039;&lt;br /&gt;
** Da durch die Wärmepumpe Überschuss &amp;quot;verheizt&amp;quot; wird, der durch das Einspeiselimit gar nicht produziert würde, kann es vorkommen, dass der Pool unangenehm warm wird.&lt;br /&gt;
** Um das zu vermeiden gibt es ein Reading, welches gesetzt wird, wenn der Pool eine gewisse Temperatur überschreitet. Dieses Setzen und Zurücksetzen erfolgt mit einer Hysterese von 0.2°C, um ein Takten der Wärmepumpe zu verhindern. Diese 0.4°C sind bei dieser Wassermenge einige Stunden.&lt;br /&gt;
** Wenn der Überhitzungsschutz aktiv ist, wird die Wärmepumpe in der Kette übersprungen.&lt;br /&gt;
** Wenn sich der Hitzeschutz während des Betriebs ändert, wird die Wärmepumpe bzw. der Pool vom Nachbarn passend ausgeschaltet und die Modi entsprechend gesetzt.&lt;br /&gt;
* &#039;&#039;&#039;On-chain:&#039;&#039;&#039;&lt;br /&gt;
** Wenn am Morgen Pump Low seit $pump_low_delay aktiv ist und alle folgenden Consumer noch nicht, dann wird die nächste Stufe freigegeben. -&amp;gt; Pump High&lt;br /&gt;
** Wenn Pump High seit $pump_high_delay aktiv ist und alle folgenden Consumer noch nicht, wird die Wärmepumpe freigegeben. -&amp;gt; Heatpump&lt;br /&gt;
** Wenn die Wärmepumpe seit $heatpump_delay aktiv ist und der letzte Consumer noch nicht, wird der letzte Verbraucher freigegeben. -&amp;gt; Pool Kurz Gleichzeitig wird die Automatik für Pump High ausgeschaltet, da dieser Verbraucher erst unterbrochen werden darf wenn die Wärmepumpe aus ist!&lt;br /&gt;
** Wenn der letzte Verbraucher aktiviert ist, wird die Automatik der Wärmepumpe deaktiviert, da ja zuerst der letzte Verbraucher (Pool Kurz) wieder ausgeschaltet werden muss, bevor der vorletzte (Wärmepumpe) wieder ausgeschaltet werden darf. Da es keinen nachfolgenden Verbraucher mehr gibt, kann hier auf ein delay verzichtet werden, da es nicht passieren kann, dass zwei Verbraucher wegen sehr hohem Überschuss unmittelbar hintereinander geschaltet werden können.&lt;br /&gt;
[[Datei:Poolsteuerung mit unterschiedlichen Automatikmodi.jpg|alternativtext=Poolsteuerung|mini|Consumer der Poolsteuerung|500x500px]]&lt;br /&gt;
&#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Es dürfen immer nur maximal zwei unterbrechbare Consumer im Automatikmodus sein. Der &amp;quot;aktuelle&amp;quot;, der ja aufgrund von fehlendem Überschuss jederzeit unterbrochen werden kann und der &amp;quot;folgende&amp;quot; Consumer, der auf das Einschalten wartet. Pump Low ist immer im Automatikmodus, da diese nie unterbrochen wird. (mode=must, interruptable=0)&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Off-chain:&#039;&#039;&#039;&lt;br /&gt;
** Wenn der letzte Verbraucher seit mindestens der delay-Zeit aufgrund von fehlendem Überschuss inaktiv war, im Automatikmodus ist und der vorherige Verbraucher nicht im Automatikmodus ist, wird der vorherige Verbraucher in den Automatikmodus geschaltet und wartet jetzt auch auf das Ausschalten. Wenn der Überschuss steigt, springt man wieder in die on-chain und der letzte Verbraucher wird eingeschaltet und die Automatik des vorletzten Verbrauchers deaktiviert.&lt;br /&gt;
** Wenn die Wärmepumpe seit mindestens der delay-Zeit aus ist, sich im Automatikmodus befind und Pump High noch nicht im Automatikmodus ist, wird die Automatik für den Nachbarpool ausgeschaltet, da er ja jetzt nicht mehr einschalten darf, da zuerst die Wärmepumpe ein sein müsste. Die Automatik von Pump High wird wieder aktiviert, da diese jetzt auf das Ausschalten wartet. Steigt der Überschuss wieder, wird in den entsprechenden On-chain Zweig gesprungen.&lt;br /&gt;
** Wenn Pump High mindestens die delay-Zeit aus ist, wird die Automatik der Wärmepumpe deaktiviert, da diese ja erst einschalten darf, wenn Pump High wieder läuft. Steigender Überschuss führt wieder zum aktivieren von Pump High und zum Sprung in den entsprechenden On-chain Zweig.&lt;br /&gt;
** Wenn Pump Low ausgeschaltet wird, wird sofort die Automatik für Pump High ausgeschaltet, da durch das spätere Ausschalten von (der evtl. wieder eingeschalteten) Pump High, hardwaremäßig wieder Pump Low aktiviert wird, aber durch SolarForecast nie ausgeschaltet wird, da Pump Low vor ein paar Minuten ja schon logisch ausgeschaltet wurde.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Es dürfen immer nur maximal zwei unterbrechbare Consumer im Automatikmodus sein. Der &amp;quot;aktuelle&amp;quot;, der ja aufgrund von neuem Überschuss jederzeit wieder gestartet werden kann und der &amp;quot;vorherige&amp;quot; Consumer, der auf das Ausschalten wartet.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Synchronisation Hardware vs. Reading:&#039;&#039;&#039;Manchmal passiert es, dass durch die etwas aufwendigen Umschaltprozesse die Hardware dem Reading nicht folgt/folgen kann. Evtl. Gründe sind Funkunterbrechung, verzögerte Umschaltung usw. Daher wird bei jedem Zyklus der Status geprüft und evtl. das event nochmal getriggert, um die Umschaltung erneut auszuführen.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hilfs-Notifys für die Automatik&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Da durch das Verwenden von swoffcond die Verbraucher beim Ausschalten auch ausgeplant werden, aber eigentlich &amp;quot;nur&amp;quot; unterbrochen werden sollen, müssen sie wieder neu eingeplant werden. Dazu sind notifys nötig. Wichtig: IDs müssen zweistellig angegeben werden!&lt;br /&gt;
&lt;br /&gt;
Wärmepumpe:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
dum_valve:heatpump:.off sleep 10; set energy_mgmt consumerNewPlanning 05&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Nachbar Pool:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
dum_valve:pool_kurz:.off sleep 10; set energy_mgmt consumerNewPlanning 07&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eine Pumpe mit einem Energiezähler als zwei Consumer verwenden&#039;&#039;&#039;  &lt;br /&gt;
&lt;br /&gt;
Die verwendete Pumpe hat vier Betriebsarten mit unterschiedlichen Leistungen. Da sie als zwei Consumer abgebildet wird, führt das dazu, dass die Leistung fälschlicherweise von SolarForecast auch zweimal gezählt wird. Die Lösung sind Dummy-Readings, welche die Leistungsdaten passend auseinander rechnen.&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
Pool_Strom_Pumpe:Pool_Pin31_monotonic_count_PowerCurrent:.* {&lt;br /&gt;
my $power = $EVTPART1;&lt;br /&gt;
my $pump_low = ReadingsVal(&#039;dum_valve&#039;,&#039;pump_low&#039;,&#039;off&#039;);&lt;br /&gt;
my $pump_high = ReadingsVal(&#039;dum_valve&#039;,&#039;pump_high&#039;,&#039;off&#039;);&lt;br /&gt;
my $power_low_sim = 250;&lt;br /&gt;
&lt;br /&gt;
if($pump_low eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; $pump_high eq &amp;quot;on&amp;quot;){	&lt;br /&gt;
	my $power_low = $power_low_sim; #fiktiver Verbrauch der niedrigen Stufe&lt;br /&gt;
	my $power_high = $power - $power_low_sim;&lt;br /&gt;
	fhem(&amp;quot;set dum_valve pump_low_power $power_low&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set dum_valve pump_high_power $power_high&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
elsif($pump_low eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; $pump_high eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set dum_valve pump_low_power $power&amp;quot;);&lt;br /&gt;
		fhem(&amp;quot;set dum_valve pump_high_power 0&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
elsif($pump_low eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $pump_high eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set dum_valve pump_low_power 0&amp;quot;);&lt;br /&gt;
		fhem(&amp;quot;set dum_valve pump_high_power 0&amp;quot;);&lt;br /&gt;
}elsif($pump_low eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $pump_high eq &amp;quot;on&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set dum_valve pump_low_power 0&amp;quot;);&lt;br /&gt;
		fhem(&amp;quot;set dum_valve pump_high_power $power&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Erläuterungen:&lt;br /&gt;
&lt;br /&gt;
* Es sind alle vier Möglichkeiten der zwei Pumpenlevel abgebildet.&lt;br /&gt;
* Wenn die Automatik deaktiviert ist und die Level 1-4 von extern geschaltet werden, zählen sie dadurch korrekterweise nicht zu diesen beiden Consumern.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notifys für das hardwaremäßige Schalten der Consumer&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Es werden einige Notifys verwendet, die die Umsetzung vom Dummy-Device auf die Hardware erledigen, da ein logisches Ein teilweise hardwaremäßig einige Dinge erfordert.&lt;br /&gt;
&lt;br /&gt;
Notify für Pump Low:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
dum_valve:pump_low:.* {&lt;br /&gt;
	if($EVTPART1 eq &amp;quot;on&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set Pool_Pin26 on; sleep 60; set Pool_Pin25 on; sleep 1; set Pool_Pin26 off&amp;quot;);&lt;br /&gt;
	}elsif($EVTPART1 eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set Pool_Pin14 off; sleep 5; set Pool_Pin12 off; sleep 15; set Pool_Pin25 off&amp;quot;);&lt;br /&gt;
	}else{&lt;br /&gt;
	Log3 (&amp;quot;Pool&amp;quot;, 4, qq{Notify Level1 - parameter is not on or off});&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterungen:&lt;br /&gt;
&lt;br /&gt;
* Beim Einschalten ist es nötig für eine Minute Pump High zu aktivieren, um Luft aus der Elektrolysezelle zu spülen, bevor Pump Low aktiv sein darf. (Pin26 und Pin25 sind die Steuerpins auf der Interfacekarte der Pumpe für die beiden Level.)&lt;br /&gt;
* Beim Ausschalten werden zuerst die Salzanlage (Pin14) und die pH-Anlage (Pin12) deaktiviert, damit noch 15 Sekunden lang die Elektrolysezelle mit frischem Wasser gespült wird, um das verbleibende Chlor auszuspülen.&amp;lt;br /&amp;gt;&lt;br /&gt;
Notify für Pump High:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
dum_valve:pump_high:.* {&lt;br /&gt;
	if($EVTPART1 eq &amp;quot;on&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set Pool_Pin26 on; sleep 1; set Pool_Pin25 off&amp;quot;);&lt;br /&gt;
	}elsif($EVTPART1 eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set Pool_Pin25 on; sleep 1; set Pool_Pin26 off&amp;quot;);&lt;br /&gt;
	}else{&lt;br /&gt;
	Log3 (&amp;quot;Pool&amp;quot;, 4, qq{Notify Level 2- parameter is not on or off});&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterung:&lt;br /&gt;
&lt;br /&gt;
* Da die Interfacekarte immer auf den höchsten Level reagiert, wird beim Umschalten zuerst Pump High aktiviert und nach einer Sekunde Pump Low deaktiviert, um ein &amp;quot;stufenloses&amp;quot; (ohne Ausschalten) Umschalten zu ermöglichen. Beim Ausschalten genauso.&lt;br /&gt;
&lt;br /&gt;
Notify für die Wärmepumpe:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
dum_valve:heatpump:.* {&lt;br /&gt;
	if($EVTPART1 eq &amp;quot;on&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set Pool_Pin6 on&amp;quot;);&lt;br /&gt;
	}elsif($EVTPART1 eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set Pool_Pin6 off&amp;quot;);&lt;br /&gt;
	}else{&lt;br /&gt;
	Log3 (&amp;quot;Pool&amp;quot;, 4, qq{Notify heatpump - parameter is not on or off});&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterung:&lt;br /&gt;
&lt;br /&gt;
* Hier reicht ein dirketes Übernehmen aus dem Dummy.&lt;br /&gt;
&lt;br /&gt;
Notify für den Nachbar-Pool:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
dum_valve:pool_kurz:.* {&lt;br /&gt;
	if($EVTPART1 eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; ReadingsVal(&amp;quot;Shelly_Plug_S_1&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;on&amp;quot;) eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set PwSw_Schalter off; sleep 3;{if(ReadingsAge(\&amp;quot;PwSw_Schalter\&amp;quot;,\&amp;quot;state\&amp;quot;,0)&amp;lt;4 &amp;amp;&amp;amp; ReadingsVal(\&amp;quot;PwSw_Schalter\&amp;quot;,\&amp;quot;state\&amp;quot;,\&amp;quot;on\&amp;quot;) eq \&amp;quot;off\&amp;quot;){fhem(\&amp;quot;set Shelly_Plug_S_1 on\&amp;quot;)}}; sleep 5; set PwSw_Schalter on&amp;quot;);&lt;br /&gt;
	}elsif($EVTPART1 eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; ReadingsVal(&amp;quot;Shelly_Plug_S_1&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set PwSw_Schalter off; sleep 3;{if(ReadingsAge(\&amp;quot;PwSw_Schalter\&amp;quot;,\&amp;quot;state\&amp;quot;,0)&amp;lt;4 &amp;amp;&amp;amp; ReadingsVal(\&amp;quot;PwSw_Schalter\&amp;quot;,\&amp;quot;state\&amp;quot;,\&amp;quot;on\&amp;quot;) eq \&amp;quot;off\&amp;quot;){fhem(\&amp;quot;set Shelly_Plug_S_1 off\&amp;quot;)}}; sleep 5; set PwSw_Schalter on&amp;quot;);&lt;br /&gt;
	}else{&lt;br /&gt;
	Log3 (&amp;quot;Pool&amp;quot;, 4, qq{Notify Pool Kurz - parameter is not on or off});&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterung&lt;br /&gt;
&lt;br /&gt;
* Da hier eine Pumpe durch einen 3-poligen Umschalter zwischen zwei Hausanspeisungen umgeschaltet wird, ist es wichtig, dass die Pumpe dazwischen &#039;&#039;&#039;gesichert&#039;&#039;&#039; zum Stehen gebracht wird! Sonst führt die rotierende Pumpe als Generator zu einem gewaltigen Kurzschluss mit der &amp;quot;fremden&amp;quot; Phase. War nicht schön ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sonderprogramme für den Poolbetrieb&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Hier ist das Aus- und Einschalten der Solarforcast-Automatik zu sehen. Es werden alle Stati gespeichert. Danach kann von beliebigen &amp;quot;Sonderfunktionen&amp;quot; (Reinigung des Filters, Massagefunktion, usw.) auf die Hardware zugegriffen werden. Wenn wieder in den Automatikmodus zurückgewechselt wird, wird hardwaremäßig wieder der Zustand der Automatik von vorher hergestellt, die Stati wieder zurück geschrieben und die Automatik wieder aktiviert.&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
##############################################&lt;br /&gt;
# Status speichern und Automatik ausschalten #&lt;br /&gt;
##############################################&lt;br /&gt;
sub PoolAutoOff{&lt;br /&gt;
	my $pump_low_id			= sprintf &amp;quot;%02d&amp;quot;, 1;&lt;br /&gt;
	my $pump_high_id		= sprintf &amp;quot;%02d&amp;quot;, 3;&lt;br /&gt;
	my $heatpump_id			= sprintf &amp;quot;%02d&amp;quot;, 5;&lt;br /&gt;
	my $pool_kurz_id		= sprintf &amp;quot;%02d&amp;quot;, 7;&lt;br /&gt;
	my $sf				= &#039;energy_mgmt&#039;;&lt;br /&gt;
&lt;br /&gt;
		&lt;br /&gt;
#	my $pump_low_state = ReadingsVal($valve_dummy,&#039;pump_low&#039;,&#039;on&#039;);&lt;br /&gt;
	my $pump_low_auto	= FHEM::SolarForecast::ConsumerVal ($sf, $pump_low_id, &#039;auto&#039;, &#039;&#039;);	# Automatikmodus der Pumpe&lt;br /&gt;
#	my $pump_high_state = ReadingsVal($valve_dummy,&#039;pump_high&#039;,&#039;on&#039;);&lt;br /&gt;
	my $pump_high_auto	= FHEM::SolarForecast::ConsumerVal ($sf, $pump_high_id, &#039;auto&#039;, &#039;&#039;);	# Automatikmodus der Pumpe&lt;br /&gt;
#	my $heatpump_state = ReadingsVal($valve_dummy,&#039;heatpump&#039;,&#039;on&#039;);&lt;br /&gt;
	my $heatpump_auto	= FHEM::SolarForecast::ConsumerVal ($sf, $heatpump_id, &#039;auto&#039;, &#039;&#039;);	# Automatikmodus der Wärmepumpe&lt;br /&gt;
#	my $pool_kurz_state = ReadingsVal($valve_dummy,&#039;pool_kurz&#039;,&#039;on&#039;);&lt;br /&gt;
	my $pool_kurz_auto	= FHEM::SolarForecast::ConsumerVal ($sf, $pool_kurz_id, &#039;auto&#039;, &#039;&#039;);	# Automatikmodus des Pools&lt;br /&gt;
	fhem(&amp;quot;set $valve_dummy pump_low_auto 0&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set $valve_dummy pump_high_auto 0&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set $valve_dummy heatpump_auto 0&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set $valve_dummy pool_kurz_auto 0&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy sf_automatic 0&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_auto_pump_low $pump_low_auto&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_auto_pump_high $pump_high_auto&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_auto_heatpump $heatpump_auto&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_auto_pool_kurz $pool_kurz_auto&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
	my $last_pump_level_1 = ReadingsVal($valve_dummy,&amp;quot;pump_low&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	my $last_pump_level_2 = ReadingsVal($valve_dummy,&amp;quot;pump_high&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	my $last_pump_level_3 = ReadingsVal($pump_level_3,&amp;quot;value&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	my $last_pump_level_4 = ReadingsVal($pump_level_4,&amp;quot;value&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	my $last_heatpump = ReadingsVal($valve_dummy,&amp;quot;heatpump&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_pump_level_1 $last_pump_level_1&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_pump_level_2 $last_pump_level_2&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_pump_level_3 $last_pump_level_3&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_pump_level_4 $last_pump_level_4&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_heatpump $last_heatpump&amp;quot;); &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
##########################################&lt;br /&gt;
# Status laden und Automatik einschalten #&lt;br /&gt;
##########################################&lt;br /&gt;
sub PoolAutoOn{&lt;br /&gt;
	my $last_pump_level_4 = ReadingsVal($valve_dummy,&amp;quot;last_pump_level_4&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	my $last_pump_level_3 = ReadingsVal($valve_dummy,&amp;quot;last_pump_level_3&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	my $last_pump_level_2 = ReadingsVal($valve_dummy,&amp;quot;last_pump_level_2&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	my $last_pump_level_1 = ReadingsVal($valve_dummy,&amp;quot;last_pump_level_1&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	my $last_heatpump = ReadingsVal($valve_dummy,&amp;quot;last_heatpump&amp;quot;,&amp;quot;off&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set $pump_level_4 $last_pump_level_4&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;set $pump_level_3 $last_pump_level_3&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set $pump_level_2 $last_pump_level_2&amp;quot;);&lt;br /&gt;
	if($last_pump_level_1 eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; $last_pump_level_2 eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $last_pump_level_3 eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $last_pump_level_4 eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set $pump_level_2 on; sleep 1; set $pump_level_1 $last_pump_level_1; sleep 28; set $pump_level_2 off; set $valve_dummy sf_automatic 1&amp;quot;);&lt;br /&gt;
	}elsif($last_pump_level_1 eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; $last_pump_level_2 eq &amp;quot;on&amp;quot; &amp;amp;&amp;amp; $last_pump_level_3 eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $last_pump_level_4 eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set $pump_level_1 off; set $valve_dummy sf_automatic 1&amp;quot;); &lt;br /&gt;
	}elsif($last_pump_level_1 eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $last_pump_level_2 eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $last_pump_level_3 eq &amp;quot;off&amp;quot; &amp;amp;&amp;amp; $last_pump_level_4 eq &amp;quot;off&amp;quot;){&lt;br /&gt;
		fhem(&amp;quot;set $pump_level_1 off; set $valve_dummy sf_automatic 1&amp;quot;); &lt;br /&gt;
	}&lt;br /&gt;
	fhem(&amp;quot;sleep 10; set $heatpump $last_heatpump&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_pump_level_1 none&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_pump_level_2 none&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_pump_level_3 none&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_pump_level_4 none&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_heatpump none&amp;quot;); &lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy valve_position normal&amp;quot;);&lt;br /&gt;
	my $last_auto_pump_low = ReadingsVal($valve_dummy,&amp;quot;last_auto_pump_low&amp;quot;,0);&lt;br /&gt;
	my $last_auto_pump_high = ReadingsVal($valve_dummy,&amp;quot;last_auto_pump_high&amp;quot;,0);&lt;br /&gt;
	my $last_auto_heatpump = ReadingsVal($valve_dummy,&amp;quot;last_auto_heatpump&amp;quot;,0);&lt;br /&gt;
	my $last_auto_pool_kurz = ReadingsVal($valve_dummy,&amp;quot;last_auto_pool_kurz&amp;quot;,0);&lt;br /&gt;
	fhem(&amp;quot;set $valve_dummy pump_low_auto $last_auto_pump_low&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set $valve_dummy pump_high_auto $last_auto_pump_high&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set $valve_dummy heatpump_auto $last_auto_heatpump&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;set $valve_dummy pool_kurz_auto $last_auto_pool_kurz&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_auto_pump_low none&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_auto_pump_high none&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_auto_heatpump none&amp;quot;);&lt;br /&gt;
	fhem(&amp;quot;setreading $valve_dummy last_auto_pool_kurz none&amp;quot;);&lt;br /&gt;
}		&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Erläuterungen:&lt;br /&gt;
&lt;br /&gt;
* pump_level_1 ist Pump Low, pump_level_2 ist Pump High, pump_level_3 und pump_level_4 sind Sonderfunktionen (Filterreinigung, Massage)&lt;br /&gt;
* Beim Wiedereinschalten von Pump Low ist es wieder nötig kurz Pump High zu aktivieren, um Luft auszuspülen.&lt;br /&gt;
* Es wird der Zustand von Solarforecast gespeichert und danach auf die Hardware geschrieben. -&amp;gt; Stellt sicher, dass nach dem Einschalten der Automatik der logical switchstate zum physical switchstate passt.&lt;br /&gt;
&lt;br /&gt;
== Tips &amp;amp; Tricks ==&lt;br /&gt;
* Interruptable verhält sich völlig anders, wenn der Perlcode 1 oder 0 liefert, als wenn man 1 oder 0 in der Config hinterlegt. -&amp;gt; siehe commandref&lt;br /&gt;
* Rückgabewert bei swoncond und swoffcond wird mit dem Readingswert verglichen und nicht dirket verwendet. Bsp. wenn das Reading 100 enthält muss auch der Rückgabewert des Perlcodes 100 liefern, um true zu ergeben.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== weiterführende Links ==&lt;br /&gt;
* Forenthema &amp;quot;{{Link2Forum|Topic=137058|LinkText=76_SolarForecast - Informationen/Ideen zu Weiterentwicklung und Support}}&amp;quot;&lt;br /&gt;
* Solcast API Toolkit: https://toolkit.solcast.com.au&lt;br /&gt;
* Kostal Plenticore 10 Plus mit SQL-Datenbank Integration ([[Kostal Plenticore 10 Plus]])&lt;br /&gt;
* Photovoltaik Ingenieurbüro Junge: https://www.ing-büro-junge.de/html/photovoltaik.html&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Energieerzeugungsmessung]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40480</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40480"/>
		<updated>2025-11-02T17:06:12Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* EPEX_Spot  RAW Device */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
Diese Wiki Seite ist noch in Bearbeitung!!!&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente.&amp;lt;br&amp;gt;&lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU. All prices are in €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
if ($h % 2 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40479</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40479"/>
		<updated>2025-11-02T17:03:44Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* EPEX_Spot  RAW Device */ next code revision of device EPEX_Boersenpreise&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
Diese Wiki Seite ist noch in Bearbeitung!!!&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute und morgen (&#039;EPEX_prices&#039; und zugehörige &#039;EPEX_timetags&#039; als Listen ) und den aktuellen Preis (&#039;EPEX_price&#039;). &amp;lt;br&amp;gt;&lt;br /&gt;
Die &#039;dayahead&#039; Preise für morgen werden gegen 14:00 am Vortag veröffentlicht. Bis dahin enthalten die Listen &#039;EPEX_prices&#039; und &#039;EPEX_timetags&#039; 96 Elemente, danach sind es 192 Elemente.&amp;lt;br&amp;gt;&lt;br /&gt;
Die timetags sind die Unix-timestamps der Viertelstunde, ab welcher der jeweilige Preis gilt.&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU. All prices are in €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price.*&lt;br /&gt;
attr EPEX_Boersenpreise get01-1Name EPEX_timetags&lt;br /&gt;
attr EPEX_Boersenpreise get01-2Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Name EPEX_prices&lt;br /&gt;
attr EPEX_Boersenpreise get01Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get01Replacement02Value {my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86400+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading02Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading02RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);;\&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices&amp;quot;,0)) &amp;gt; $time-86400) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	};;\&lt;br /&gt;
fhem(&amp;quot;get $name EPEX_prices&amp;quot;);;\&lt;br /&gt;
#if ($h % 1 == 0 and $m == 0) {fhem(&amp;quot;get $name EPEX_prices&amp;quot;)};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading02Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement02Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise showError 1&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
attr EPEX_Boersenpreise userReadings no_of_data:EPEX_prices.* {split(/,/,ReadingsVal(&amp;quot;EPEX_Boersenpreise&amp;quot;,&amp;quot;EPEX_prices&amp;quot;,&amp;quot;&amp;quot;))}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40471</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40471"/>
		<updated>2025-10-28T04:44:31Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* EPEX_Spot  RAW Device */  Code: bug-fix&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
Diese Wiki Seite ist noch in Bearbeitung!!!&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute (&#039;EPEX_prices_day&#039;), für morgen (&#039;EPEX_prices_dayahead&#039;) und den aktuellen Preis (&#039;EPEX_price&#039;). &lt;br /&gt;
Die dayahead Preise werden in der Regel gegen 14:00 am Vortag veröffentlicht.&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Boersenpreise DbLogInclude EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU. All prices are in €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise disable 0&lt;br /&gt;
attr EPEX_Boersenpreise event-on-update-reading EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise get02-1Name EPEX_timetags_day&lt;br /&gt;
attr EPEX_Boersenpreise get02-2Name EPEX_prices_day&lt;br /&gt;
attr EPEX_Boersenpreise get02Name EPEX_prices_day&lt;br /&gt;
attr EPEX_Boersenpreise get02Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get02Replacement01Value {&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise get03-1Name EPEX_timetags_dayahead&lt;br /&gt;
attr EPEX_Boersenpreise get03-2Name EPEX_prices_dayahead&lt;br /&gt;
attr EPEX_Boersenpreise get03Name EPEX_prices_dayahead&lt;br /&gt;
attr EPEX_Boersenpreise get03Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Boersenpreise get03Replacement01Value {fhem(&amp;quot;setreading $name EPEX_prices_dayahead not yet published&amp;quot;);;\&lt;br /&gt;
fhem(&amp;quot;setreading $name EPEX_timetags_dayahead not yet published&amp;quot;);;\&lt;br /&gt;
my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=86400+int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise group EPEX&lt;br /&gt;
attr EPEX_Boersenpreise reading01Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise reading01RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,0,0,$D,$M,$Y);; \&lt;br /&gt;
my $price = &amp;quot;error&amp;quot;;;\&lt;br /&gt;
if (time_str2num(ReadingsTimestamp($name, &amp;quot;EPEX_prices_day&amp;quot;,0)) &amp;gt; $time) {\&lt;br /&gt;
	$time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
	my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags_day&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices_day&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
	my %map;; @map{@times} = @prices;; \&lt;br /&gt;
	if (exists $map{$time}) {$price = $map{$time}};;\&lt;br /&gt;
	if (($h == 14) and ($m &amp;gt; 14)) {\&lt;br /&gt;
	   fhem(&amp;quot;defmod temp_at1 at +00:00:10 get $name EPEX_prices_dayahead&amp;quot;)};;\&lt;br /&gt;
	}\&lt;br /&gt;
else {\&lt;br /&gt;
	fhem(&amp;quot;get $name EPEX_prices_day&amp;quot;);;\&lt;br /&gt;
	fhem(&amp;quot;defmod temp_at2 at +00:00:15 set $name reread&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
$price}&lt;br /&gt;
attr EPEX_Boersenpreise reading01Regex .&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Regex URL&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise room Energie&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise timeout 5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40470</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40470"/>
		<updated>2025-10-27T10:10:22Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* EPEX_Spot  RAW Device */  next code revision of EPEX_Test device&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
Diese Wiki Seite ist noch in Bearbeitung!!!&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute (&#039;EPEX_prices_day&#039;), für morgen (&#039;EPEX_prices_dayahead&#039;) und den aktuellen Preis (&#039;EPEX_price&#039;). &lt;br /&gt;
Die dayahead Preise werden in der Regel gegen 14:00 am Vortag veröffentlicht.&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Test HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Test DbLogInclude EPEX_price&lt;br /&gt;
attr EPEX_Test alignTime 00:00&lt;br /&gt;
attr EPEX_Test comment This device queries prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU. All prices are in €/MWh&lt;br /&gt;
attr EPEX_Test disable 0&lt;br /&gt;
attr EPEX_Test event-on-update-reading EPEX_price&lt;br /&gt;
attr EPEX_Test get02-1Name EPEX_timetags_day&lt;br /&gt;
attr EPEX_Test get02-2Name EPEX_prices_day&lt;br /&gt;
attr EPEX_Test get02Name EPEX_prices_day&lt;br /&gt;
attr EPEX_Test get02Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Test get02Replacement01Value {&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;quot;}&lt;br /&gt;
attr EPEX_Test get03-1Name EPEX_timetags_dayahead&lt;br /&gt;
attr EPEX_Test get03-2Name EPEX_prices_dayahead&lt;br /&gt;
attr EPEX_Test get03Name EPEX_prices_dayahead&lt;br /&gt;
attr EPEX_Test get03Regex unix_seconds&amp;quot;:\[(.*?)\],&amp;quot;price&amp;quot;:\[(.*?)\]&lt;br /&gt;
attr EPEX_Test get03Replacement01Value {fhem(&amp;quot;setreading $name EPEX_prices_dayahead not yet published&amp;quot;);;\&lt;br /&gt;
fhem(&amp;quot;setreading $name EPEX_timetags_dayahead not yet published&amp;quot;);;\&lt;br /&gt;
my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=86400+int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86399)}&lt;br /&gt;
attr EPEX_Test group EPEX&lt;br /&gt;
attr EPEX_Test reading01Name EPEX_price&lt;br /&gt;
attr EPEX_Test reading01RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
if (($h == 14) and ($m &amp;gt; 14)) {\&lt;br /&gt;
   fhem(&amp;quot;defmod temp_at1 at +00:00:10 get $name EPEX_prices_dayahead&amp;quot;)\&lt;br /&gt;
   };; \&lt;br /&gt;
if (($h == 23) and ($m &amp;gt; 45)) {\&lt;br /&gt;
   my $a=ReadingsVal($name,&amp;quot;EPEX_prices_dayahead&amp;quot;,&amp;quot;&amp;quot;);;\&lt;br /&gt;
   fhem(&amp;quot;setreading $name EPEX_prices_day $a&amp;quot;);; \&lt;br /&gt;
   $a=ReadingsVal($name,&amp;quot;EPEX_timetags_dayahead&amp;quot;,&amp;quot;&amp;quot;);;\&lt;br /&gt;
   fhem(&amp;quot;setreading $name EPEX_timetags_day $a&amp;quot;);; \&lt;br /&gt;
   };;\&lt;br /&gt;
my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_timetags_day&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices_day&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
my %map;; @map{@times} = @prices;; my $price;;\&lt;br /&gt;
if (exists $map{$time}) {$price = $map{$time}} else {$price = &amp;quot;error&amp;quot;};;$price}&lt;br /&gt;
attr EPEX_Test reading01Regex .&lt;br /&gt;
attr EPEX_Test replacement01Mode expression&lt;br /&gt;
attr EPEX_Test replacement01Regex URL&lt;br /&gt;
attr EPEX_Test replacement01Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Test room Energie&lt;br /&gt;
attr EPEX_Test stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Test timeout 5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40469</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40469"/>
		<updated>2025-10-27T03:25:05Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* EPEX_Spot  RAW Device */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
Diese Wiki Seite ist noch in Bearbeitung!!!&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute (&#039;EPEX_prices_day&#039;), für morgen (&#039;EPEX_prices_dayahead&#039;) und den aktuellen Preis (&#039;EPEX_price&#039;). &lt;br /&gt;
Die dayahead Preise werden in der Regel gegen 14:00 am Vortag veröffentlicht.&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Test HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Test DbLogInclude EPEX_price&lt;br /&gt;
attr EPEX_Test alignTime 00:00&lt;br /&gt;
attr EPEX_Test comment This device queries prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU. All prices are in €/MWh&lt;br /&gt;
attr EPEX_Test disable 0&lt;br /&gt;
attr EPEX_Test event-on-update-reading EPEX_price&lt;br /&gt;
attr EPEX_Test get02JSON price&lt;br /&gt;
attr EPEX_Test get02Name EPEX_prices_day&lt;br /&gt;
attr EPEX_Test get02RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Test get02Replacement01Value {&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;quot;}&lt;br /&gt;
attr EPEX_Test get03JSON price&lt;br /&gt;
attr EPEX_Test get03Name EPEX_prices_dayahead&lt;br /&gt;
attr EPEX_Test get03RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Test get03Replacement01Value {fhem(&amp;quot;setreading $name EPEX_prices_dayahead not yet published&amp;quot;);;my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=86400+int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86399)}&lt;br /&gt;
attr EPEX_Test get04JSON unix_seconds&lt;br /&gt;
attr EPEX_Test get04Name EPEX_times_day&lt;br /&gt;
attr EPEX_Test get04RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Test get04Replacement01Value {&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;quot;}&lt;br /&gt;
attr EPEX_Test get05JSON unix_seconds&lt;br /&gt;
attr EPEX_Test get05Name EPEX_times_dayahead&lt;br /&gt;
attr EPEX_Test get05RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Test get05Replacement01Value {fhem(&amp;quot;setreading $name EPEX_prices_dayahead not yet published&amp;quot;);;my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=86400+int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86399)}&lt;br /&gt;
attr EPEX_Test group EPEX&lt;br /&gt;
attr EPEX_Test reading01Name EPEX_price&lt;br /&gt;
attr EPEX_Test reading01RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
if (($h == 14) and ($m &amp;gt; 14)) {\&lt;br /&gt;
   fhem(&amp;quot;defmod temp_at1 at +00:00:10 get $name EPEX_prices_dayahead&amp;quot;);; \&lt;br /&gt;
   fhem(&amp;quot;defmod temp_at2 at +00:00:20 get $name EPEX_times_dayahead&amp;quot;)};;\&lt;br /&gt;
if (($h == 23) and ($m &amp;gt; 45)) {\&lt;br /&gt;
   my $a=ReadingsVal($name,&amp;quot;EPEX_prices_dayahead&amp;quot;,&amp;quot;&amp;quot;);;\&lt;br /&gt;
   fhem(&amp;quot;setreading $name EPEX_prices_day $a&amp;quot;);; \&lt;br /&gt;
   $a=ReadingsVal($name,&amp;quot;EPEX_times_dayahead&amp;quot;,&amp;quot;&amp;quot;);;\&lt;br /&gt;
   fhem(&amp;quot;setreading $name EPEX_times_day $a&amp;quot;);; \&lt;br /&gt;
   };;\&lt;br /&gt;
my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_times_day&amp;quot;,&amp;quot;-&amp;quot;)1);;\&lt;br /&gt;
my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices_day&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
my %map;; @map{@times} = @prices;; my $price;;\&lt;br /&gt;
if (exists $map{$time}) {$price = $map{$time}} else {$price = &amp;quot;error&amp;quot;};;$price}&lt;br /&gt;
attr EPEX_Test reading01Regex .&lt;br /&gt;
attr EPEX_Test replacement01Mode expression&lt;br /&gt;
attr EPEX_Test replacement01Regex URL&lt;br /&gt;
attr EPEX_Test replacement01Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Test room Energie&lt;br /&gt;
attr EPEX_Test stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Test timeout 5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40468</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40468"/>
		<updated>2025-10-27T03:22:37Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* EPEX_Spot  RAW Device */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
Diese Wiki Seite ist noch in Bearbeitung!!!&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute (&#039;EPEX_prices_day&#039;), für morgen (&#039;EPEX_prices_dayahead&#039;) und den aktuellen Preis (&#039;EPEX_price&#039;). &lt;br /&gt;
Die dayahead Preise werden in der Regel gegen 14:00 am Vortag veröffentlicht.&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Test HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Test DbLogInclude EPEX_price&lt;br /&gt;
attr EPEX_Test alignTime 00:00&lt;br /&gt;
attr EPEX_Test comment This device queries prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU. All prices are in €/MWh&lt;br /&gt;
attr EPEX_Test disable 0&lt;br /&gt;
attr EPEX_Test event-on-update-reading EPEX_price&lt;br /&gt;
attr EPEX_Test get02JSON price&lt;br /&gt;
attr EPEX_Test get02Name EPEX_prices_day&lt;br /&gt;
attr EPEX_Test get02RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Test get02Replacement01Value {&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;quot;}&lt;br /&gt;
attr EPEX_Test get03JSON price&lt;br /&gt;
attr EPEX_Test get03Name EPEX_prices_dayahead&lt;br /&gt;
attr EPEX_Test get03RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Test get03Replacement01Value {fhem(&amp;quot;setreading $name EPEX_prices_dayahead not yet published&amp;quot;);;my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=86400+int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86399)}&lt;br /&gt;
attr EPEX_Test get04JSON unix_seconds&lt;br /&gt;
attr EPEX_Test get04Name EPEX_times_day&lt;br /&gt;
attr EPEX_Test get04RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Test get04Replacement01Value {&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;quot;}&lt;br /&gt;
attr EPEX_Test get05JSON unix_seconds&lt;br /&gt;
attr EPEX_Test get05Name EPEX_times_dayahead&lt;br /&gt;
attr EPEX_Test get05RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Test get05Replacement01Value {fhem(&amp;quot;setreading $name EPEX_prices_dayahead not yet published&amp;quot;);;my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=86400+int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86399)}&lt;br /&gt;
attr EPEX_Test group EPEX&lt;br /&gt;
attr EPEX_Test reading01Name EPEX_price&lt;br /&gt;
attr EPEX_Test reading01RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
if (($h == 14) and ($m &amp;gt; 14)) {\&lt;br /&gt;
   fhem(&amp;quot;defmod temp_at1 at +00:00:10 get $name EPEX_prices_dayahead&amp;quot;);; \&lt;br /&gt;
   fhem(&amp;quot;defmod temp_at2 at +00:00:20 get $name EPEX_times_dayahead&amp;quot;)};;\&lt;br /&gt;
if (($h == 23) and ($m &amp;gt; 45)) {\&lt;br /&gt;
   my $a=ReadingsVal($name,&amp;quot;EPEX_prices_dayahead&amp;quot;,&amp;quot;&amp;quot;);;\&lt;br /&gt;
   fhem(&amp;quot;setreading $name EPEX_prices_day $a&amp;quot;);; \&lt;br /&gt;
   $a=ReadingsVal($name,&amp;quot;EPEX_times_dayahead&amp;quot;,&amp;quot;&amp;quot;);;\&lt;br /&gt;
   fhem(&amp;quot;setreading $name EPEX_times_day $a&amp;quot;);; \&lt;br /&gt;
   };;\&lt;br /&gt;
my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_times_day&amp;quot;,&amp;quot;-&amp;quot;)1);;\&lt;br /&gt;
my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices_day&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
my %map;; @map{@times} = @prices;; my $price;;\&lt;br /&gt;
if (exists $map{$time}) {$price = $map{$time}} else {$price = &amp;quot;error&amp;quot;};;$price}&lt;br /&gt;
attr EPEX_Test reading01Regex .&lt;br /&gt;
attr EPEX_Test replacement01Mode expression&lt;br /&gt;
attr EPEX_Test replacement01Regex URL&lt;br /&gt;
attr EPEX_Test replacement01Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Test room Energie&lt;br /&gt;
attr EPEX_Test stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Test timeout 5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt. Davon abgesehen ist es soowieso überflüssig, die sich nur einmal täglich ändernden &#039;prices_day&#039; und &#039;prices_dayahead&#039; Wert zu oft auszulesen.&lt;br /&gt;
Folgendes &#039;at&#039; sorgt dafür, dass beide Preis_Listen nur 1x pro Tag gelesen werden:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
define get_EPEX_prices at *00:03:00 get EPEX_Boersenpreise EPEX_prices_day;;setreading EPEX_Boersenpreise EPEX_prices_dayahead not yet published;;define get_EPEX_prices_dayahead at 15:03 get EPEX_Boersenpreise EPEX_prices_dayahead&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dieses &#039;at&#039; liest täglich kurz nach Mitternacht die &#039;day&#039; Preise und erzeugt ein einmal-&#039;at&#039;, das kurz nach 15:00 die &#039;dayahead&#039; Preise liest.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40467</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40467"/>
		<updated>2025-10-27T03:16:47Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* EPEX_Spot  RAW Device */  Code ersetzt (falsch sortierte Preise in alter Version)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
Diese Wiki Seite ist noch in Bearbeitung!!!&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute (&#039;EPEX_prices_day&#039;), für morgen (&#039;EPEX_prices_dayahead&#039;) und den aktuellen Preis (&#039;EPEX_price&#039;). &lt;br /&gt;
Die dayahead Preise werden in der Regel gegen 14:00 am Vortag veröffentlicht.&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Test HTTPMOD URL 900&lt;br /&gt;
attr EPEX_Test DbLogInclude EPEX_price&lt;br /&gt;
attr EPEX_Test alignTime 00:00&lt;br /&gt;
attr EPEX_Test comment This device queries prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
The quarter-hour day and dayahead prices are given as lists of 96 elements each, the first price element being valid for the first quarter-hour 00:00 to 00:15, and so on.\&lt;br /&gt;
All prices are in €/MWh&lt;br /&gt;
attr EPEX_Test disable 0&lt;br /&gt;
attr EPEX_Test event-on-update-reading EPEX_price&lt;br /&gt;
attr EPEX_Test get02JSON price&lt;br /&gt;
attr EPEX_Test get02Name EPEX_prices_day&lt;br /&gt;
attr EPEX_Test get02RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Test get02Replacement01Value {&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;quot;}&lt;br /&gt;
attr EPEX_Test get03JSON price&lt;br /&gt;
attr EPEX_Test get03Name EPEX_prices_dayahead&lt;br /&gt;
attr EPEX_Test get03RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Test get03Replacement01Value {fhem(&amp;quot;setreading $name EPEX_prices_dayahead not yet published&amp;quot;);;my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=86400+int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86399)}&lt;br /&gt;
attr EPEX_Test get04JSON unix_seconds&lt;br /&gt;
attr EPEX_Test get04Name EPEX_times_day&lt;br /&gt;
attr EPEX_Test get04RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Test get04Replacement01Value {&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;quot;}&lt;br /&gt;
attr EPEX_Test get05JSON unix_seconds&lt;br /&gt;
attr EPEX_Test get05Name EPEX_times_dayahead&lt;br /&gt;
attr EPEX_Test get05RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Test get05Replacement01Value {fhem(&amp;quot;setreading $name EPEX_prices_dayahead not yet published&amp;quot;);;my ($s,$m,$h,$D,$M,$Y)=localtime();;my $start=86400+int(fhemTimeLocal(0,0,0,$D,$M,$Y));;&amp;quot;https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start).&amp;quot;&amp;amp;end=&amp;quot;.sprintf(&amp;quot;%d&amp;quot;,$start+86399)}&lt;br /&gt;
attr EPEX_Test group EPEX&lt;br /&gt;
attr EPEX_Test reading01Name EPEX_price&lt;br /&gt;
attr EPEX_Test reading01RecombineExpr {my ($s,$m,$h,$D,$M,$Y)=localtime();; my $time=fhemTimeLocal(0,15*int($m/15),$h,$D,$M,$Y);;\&lt;br /&gt;
if (($h == 14) and ($m &amp;gt; 14)) {\&lt;br /&gt;
   fhem(&amp;quot;defmod temp_at1 at +00:00:10 get $name EPEX_prices_dayahead&amp;quot;);; \&lt;br /&gt;
   fhem(&amp;quot;defmod temp_at2 at +00:00:20 get $name EPEX_times_dayahead&amp;quot;)};;\&lt;br /&gt;
if (($h == 23) and ($m &amp;gt; 45)) {\&lt;br /&gt;
   my $a=ReadingsVal($name,&amp;quot;EPEX_prices_dayahead&amp;quot;,&amp;quot;&amp;quot;);;\&lt;br /&gt;
   fhem(&amp;quot;setreading $name EPEX_prices_day $a&amp;quot;);; \&lt;br /&gt;
   $a=ReadingsVal($name,&amp;quot;EPEX_times_dayahead&amp;quot;,&amp;quot;&amp;quot;);;\&lt;br /&gt;
   fhem(&amp;quot;setreading $name EPEX_times_day $a&amp;quot;);; \&lt;br /&gt;
   };;\&lt;br /&gt;
my @times  = split(/,/,ReadingsVal($name,&amp;quot;EPEX_times_day&amp;quot;,&amp;quot;-&amp;quot;)1);;\&lt;br /&gt;
my @prices = split(/,/,ReadingsVal($name,&amp;quot;EPEX_prices_day&amp;quot;,&amp;quot;-1&amp;quot;));;\&lt;br /&gt;
my %map;; @map{@times} = @prices;; my $price;;\&lt;br /&gt;
if (exists $map{$time}) {$price = $map{$time}} else {$price = &amp;quot;error&amp;quot;};;$price}&lt;br /&gt;
attr EPEX_Test reading01Regex .&lt;br /&gt;
attr EPEX_Test replacement01Mode expression&lt;br /&gt;
attr EPEX_Test replacement01Regex URL&lt;br /&gt;
attr EPEX_Test replacement01Value {&amp;quot;http://localhost/&amp;quot;}&lt;br /&gt;
attr EPEX_Test room Energie&lt;br /&gt;
attr EPEX_Test stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Test timeout 5&lt;br /&gt;
attr EPEX_Test verbose 3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt. Davon abgesehen ist es soowieso überflüssig, die sich nur einmal täglich ändernden &#039;prices_day&#039; und &#039;prices_dayahead&#039; Wert zu oft auszulesen.&lt;br /&gt;
Folgendes &#039;at&#039; sorgt dafür, dass beide Preis_Listen nur 1x pro Tag gelesen werden:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
define get_EPEX_prices at *00:03:00 get EPEX_Boersenpreise EPEX_prices_day;;setreading EPEX_Boersenpreise EPEX_prices_dayahead not yet published;;define get_EPEX_prices_dayahead at 15:03 get EPEX_Boersenpreise EPEX_prices_dayahead&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dieses &#039;at&#039; liest täglich kurz nach Mitternacht die &#039;day&#039; Preise und erzeugt ein einmal-&#039;at&#039;, das kurz nach 15:00 die &#039;dayahead&#039; Preise liest.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40461</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40461"/>
		<updated>2025-10-25T18:52:55Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: code-Block ausklappbar gemacht&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
Diese Wiki Seite ist noch in Bearbeitung!!!&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute (&#039;EPEX_prices_day&#039;), für morgen (&#039;EPEX_prices_dayahead&#039;) und den aktuellen Preis (&#039;EPEX_price&#039;). &lt;br /&gt;
Die dayahead Preise werden in der Regel gegen 14:00 am Vortag veröffentlicht.&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code für device &#039;&#039;EPEX_Boersenpreise&#039;&#039;:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD https://api.energy-charts.info/price?bzn=DE-LUtimerange 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00:30&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
The quarter-hour day and dayahead prices are given as lists of 96 elements each, the first price element being valid for the first quarter-hour 00:00 to 00:15, and so on.\&lt;br /&gt;
All prices are in €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise get02JSON price&lt;br /&gt;
attr EPEX_Boersenpreise get02Name EPEX_prices_day&lt;br /&gt;
attr EPEX_Boersenpreise get02RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Boersenpreise get02Replacement01Value {&amp;quot;&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise get03JSON price&lt;br /&gt;
attr EPEX_Boersenpreise get03Name EPEX_prices_dayahead&lt;br /&gt;
attr EPEX_Boersenpreise get03RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Boersenpreise get03Replacement01Value {fhem(&amp;quot;setreading $name EPEX_prices_dayahead not yet published&amp;quot;);;;;my ($sec, $min, $hour, $mday, $month, $year) = localtime();;;;my $start = 86400+int(fhemTimeLocal(0,0,0, $mday, $month, $year));;;;&amp;quot;&amp;amp;start=&amp;quot;.$start.&amp;quot;&amp;amp;end=&amp;quot;.($start+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise reading01JSON price&lt;br /&gt;
attr EPEX_Boersenpreise reading01Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Regex timerange&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Value {my ($sec, $min, $hour, $mday, $month, $year) = localtime();;;;&amp;quot;&amp;amp;start=&amp;quot;.900 * (int(fhemTimeLocal(0, $min, $hour, $mday, $month, $year) / 900))}&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise verbose 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt. Davon abgesehen ist es soowieso überflüssig, die sich nur einmal täglich ändernden &#039;prices_day&#039; und &#039;prices_dayahead&#039; Wert zu oft auszulesen.&lt;br /&gt;
Folgendes &#039;at&#039; sorgt dafür, dass beide Preis_Listen nur 1x pro Tag gelesen werden:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
define get_EPEX_prices at *00:03:00 get EPEX_Boersenpreise EPEX_prices_day;;setreading EPEX_Boersenpreise EPEX_prices_dayahead not yet published;;define get_EPEX_prices_dayahead at 15:03 get EPEX_Boersenpreise EPEX_prices_dayahead&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dieses &#039;at&#039; liest täglich kurz nach Mitternacht die &#039;day&#039; Preise und erzeugt ein einmal-&#039;at&#039;, das kurz nach 15:00 die &#039;dayahead&#039; Preise liest.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40460</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40460"/>
		<updated>2025-10-25T18:49:36Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* EPEX_Spot  RAW Device */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
Diese Wiki Seite ist noch in Bearbeitung!!!&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgerufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen&#039;&#039;&#039; bekommen nur Kunden, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrage 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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen Fixkosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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 eventuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements werden 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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute (&#039;EPEX_prices_day&#039;), für morgen (&#039;EPEX_prices_dayahead&#039;) und den aktuellen Preis (&#039;EPEX_price&#039;). &lt;br /&gt;
Die dayahead Preise werden in der Regel gegen 14:00 am Vortag veröffentlicht.&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot;&amp;gt;&lt;br /&gt;
Code:&amp;lt;div class=&amp;quot;mw-collapsible-content&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD https://api.energy-charts.info/price?bzn=DE-LUtimerange 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00:30&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
The quarter-hour day and dayahead prices are given as lists of 96 elements each, the first price element being valid for the first quarter-hour 00:00 to 00:15, and so on.\&lt;br /&gt;
All prices are in €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise get02JSON price&lt;br /&gt;
attr EPEX_Boersenpreise get02Name EPEX_prices_day&lt;br /&gt;
attr EPEX_Boersenpreise get02RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Boersenpreise get02Replacement01Value {&amp;quot;&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise get03JSON price&lt;br /&gt;
attr EPEX_Boersenpreise get03Name EPEX_prices_dayahead&lt;br /&gt;
attr EPEX_Boersenpreise get03RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Boersenpreise get03Replacement01Value {fhem(&amp;quot;setreading $name EPEX_prices_dayahead not yet published&amp;quot;);;;;my ($sec, $min, $hour, $mday, $month, $year) = localtime();;;;my $start = 86400+int(fhemTimeLocal(0,0,0, $mday, $month, $year));;;;&amp;quot;&amp;amp;start=&amp;quot;.$start.&amp;quot;&amp;amp;end=&amp;quot;.($start+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise reading01JSON price&lt;br /&gt;
attr EPEX_Boersenpreise reading01Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Regex timerange&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Value {my ($sec, $min, $hour, $mday, $month, $year) = localtime();;;;&amp;quot;&amp;amp;start=&amp;quot;.900 * (int(fhemTimeLocal(0, $min, $hour, $mday, $month, $year) / 900))}&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise verbose 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt. Davon abgesehen ist es soowieso überflüssig, die sich nur einmal täglich ändernden &#039;prices_day&#039; und &#039;prices_dayahead&#039; Wert zu oft auszulesen.&lt;br /&gt;
Folgendes &#039;at&#039; sorgt dafür, dass beide Preis_Listen nur 1x pro Tag gelesen werden:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
define get_EPEX_prices at *00:03:00 get EPEX_Boersenpreise EPEX_prices_day;;setreading EPEX_Boersenpreise EPEX_prices_dayahead not yet published;;define get_EPEX_prices_dayahead at 15:03 get EPEX_Boersenpreise EPEX_prices_dayahead&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dieses &#039;at&#039; liest täglich kurz nach Mitternacht die &#039;day&#039; Preise und erzeugt ein einmal-&#039;at&#039;, das kurz nach 15:00 die &#039;dayahead&#039; Preise liest.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Diskussion:Solaranlage_Komplettbeispiel_Fronius_BYD&amp;diff=40459</id>
		<title>Diskussion:Solaranlage Komplettbeispiel Fronius BYD</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Diskussion:Solaranlage_Komplettbeispiel_Fronius_BYD&amp;diff=40459"/>
		<updated>2025-10-25T18:42:37Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: Neuer Abschnitt /* Abhängigkeit von Autolade_Calculator */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;So Leute, die Grundstruktur steht erst mal.&lt;br /&gt;
Soll der Titel so bleiben, oder geändert werden?&lt;br /&gt;
&lt;br /&gt;
== Abhängigkeit von Autolade_Calculator ==&lt;br /&gt;
&lt;br /&gt;
Wenn man den Code im Abschnitt &amp;quot;Fronius_Symo in FHEM&amp;quot; 1:1 übernimmt, führt eine Abhängigkeit von &amp;quot;Autolade_Calculator&amp;quot; in userReadings zu Einträgen im logfile, wenn kein device &amp;quot;Autolade_Calculator&amp;quot; definiert ist:&lt;br /&gt;
&lt;br /&gt;
&amp;quot;User_Consumed_EN:PowerFlow_Site_P_PV.* {ReadingsVal($name,&amp;quot;PowerFlow_Site_P_PV&amp;quot;,&amp;quot;&amp;quot;)-ReadingsVal(&amp;quot;Autolade_Calculator&amp;quot;,&amp;quot;Auto_Ladung_av&amp;quot;,&amp;quot;&amp;quot;)+ReadingsVal($name,&amp;quot;PowerFlow_Site_P_Grid&amp;quot;,&amp;quot;&amp;quot;)+ReadingsVal($name,&amp;quot;PowerFlow_Site_P_Akku&amp;quot;,&amp;quot;&amp;quot;)},&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Ich schlage vor, die Zeile zu entfernen (oder zu erläutern, welches device da verlangt wird).&lt;br /&gt;
&lt;br /&gt;
(Anmerkung: Bei mir irritierte zusätzlich, dass die log-Einträge selbst mit verbose=0 auftraten)&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40453</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40453"/>
		<updated>2025-10-20T16:32:52Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* EPEX_Spot  RAW Device */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
Diese Wiki Seite ist noch in Bearbeitung!!!&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgrufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen bekommen nur Kunden&#039;&#039;&#039;, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht untertsützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen fix Kosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute (&#039;EPEX_prices_day&#039;), für morgen (&#039;EPEX_prices_dayahead&#039;) und den aktuellen Preis (&#039;EPEX_price&#039;). &lt;br /&gt;
Die dayahead Preise werden in der Regel gegen 14:00 am Vortag veröffentlicht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD https://api.energy-charts.info/price?bzn=DE-LUtimerange 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00:30&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
The quarter-hour day and dayahead prices are given as lists of 96 elements each, the first price element being valid for the first quarter-hour 00:00 to 00:15, and so on.\&lt;br /&gt;
All prices are in €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise get02JSON price&lt;br /&gt;
attr EPEX_Boersenpreise get02Name EPEX_prices_day&lt;br /&gt;
attr EPEX_Boersenpreise get02RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Boersenpreise get02Replacement01Value {&amp;quot;&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise get03JSON price&lt;br /&gt;
attr EPEX_Boersenpreise get03Name EPEX_prices_dayahead&lt;br /&gt;
attr EPEX_Boersenpreise get03RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Boersenpreise get03Replacement01Value {fhem(&amp;quot;setreading $name EPEX_prices_dayahead not yet published&amp;quot;);;;;my ($sec, $min, $hour, $mday, $month, $year) = localtime();;;;my $start = 86400+int(fhemTimeLocal(0,0,0, $mday, $month, $year));;;;&amp;quot;&amp;amp;start=&amp;quot;.$start.&amp;quot;&amp;amp;end=&amp;quot;.($start+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise reading01JSON price&lt;br /&gt;
attr EPEX_Boersenpreise reading01Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Regex timerange&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Value {my ($sec, $min, $hour, $mday, $month, $year) = localtime();;;;&amp;quot;&amp;amp;start=&amp;quot;.900 * (int(fhemTimeLocal(0, $min, $hour, $mday, $month, $year) / 900))}&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise verbose 2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt. Davon abgesehen ist es soowieso überflüssig, die sich nur einmal täglich ändernden &#039;prices_day&#039; und &#039;prices_dayahead&#039; Wert zu oft auszulesen.&lt;br /&gt;
Folgendes &#039;at&#039; sorgt dafür, dass beide Preis_Listen nur 1x pro Tag gelesen werden:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
define get_EPEX_prices at *00:03:00 get EPEX_Boersenpreise EPEX_prices_day;;setreading EPEX_Boersenpreise EPEX_prices_dayahead not yet published;;define get_EPEX_prices_dayahead at 15:03 get EPEX_Boersenpreise EPEX_prices_dayahead&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dieses &#039;at&#039; liest täglich kurz nach Mitternacht die &#039;day&#039; Preise und erzeugt ein einmal-&#039;at&#039;, dass kurz nach 15:00 die &#039;dayahead&#039; Preise liest.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40452</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40452"/>
		<updated>2025-10-20T16:30:24Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* EPEX_Spot  RAW Device */  getxxxPoll entfernt: &amp;#039;at&amp;#039; device fürs Auslesen von Preis_Listen hinzugefügt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
Diese Wiki Seite ist noch in Bearbeitung!!!&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgrufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen bekommen nur Kunden&#039;&#039;&#039;, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht untertsützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen fix Kosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute (&#039;EPEX_prices_day&#039;), für morgen (&#039;EPEX_prices_dayahead&#039;) und den aktuellen Preis (&#039;EPEX_price&#039;). &lt;br /&gt;
Die dayahead Preise werden in der Regel gegen 14:00 am Vortag veröffentlicht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD https://api.energy-charts.info/price?bzn=DE-LUtimerange 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00:30&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
The quarter-hour day and dayahead prices are given as lists of 96 elements each, the first price element being valid for the first quarter-hour 00:00 to 00:15, and so on.\&lt;br /&gt;
All prices are in €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise get02JSON price&lt;br /&gt;
attr EPEX_Boersenpreise get02Name EPEX_prices_day&lt;br /&gt;
attr EPEX_Boersenpreise get02RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Boersenpreise get02Replacement01Value {&amp;quot;&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise get03JSON price&lt;br /&gt;
attr EPEX_Boersenpreise get03Name EPEX_prices_dayahead&lt;br /&gt;
attr EPEX_Boersenpreise get03RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Boersenpreise get03Replacement01Value {fhem(&amp;quot;setreading $name EPEX_prices_dayahead not yet published&amp;quot;);;;;my ($sec, $min, $hour, $mday, $month, $year) = localtime();;;;my $start = 86400+int(fhemTimeLocal(0,0,0, $mday, $month, $year));;;;&amp;quot;&amp;amp;start=&amp;quot;.$start.&amp;quot;&amp;amp;end=&amp;quot;.($start+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise reading01JSON price&lt;br /&gt;
attr EPEX_Boersenpreise reading01Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Regex timerange&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Value {my ($sec, $min, $hour, $mday, $month, $year) = localtime();;;;&amp;quot;&amp;amp;start=&amp;quot;.900 * (int(fhemTimeLocal(0, $min, $hour, $mday, $month, $year) / 900))}&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise verbose 2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Weder get02Poll noch get03Poll sollten auf 1 gesetzt sein, da es dann zu gelegentlichen timeout-Fehlern kommt. Davon abgesehen ist es soowieso überflüssig, die sich nur einmal täglich ändernden &#039;prices_day&#039; und &#039;prices_dayahead&#039; Wert zu oft auszulesen.&lt;br /&gt;
Folgendes &#039;at&#039; sorgt dafür, dass beide Preis_Listen nur 1x pro Tag gelesen werden:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
define get_EPEX_prices at *00:03:00 get EPEX_Boersenpreise EPEX_prices_day;;define get_EPEX_prices_dayahead at 15:03 get EPEX_Boersenpreise EPEX_prices_dayahead&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dieses &#039;at&#039; liest täglich kurz nach Mitternacht die &#039;day&#039; Preise und erzeugt ein einmal-&#039;at&#039;, dass kurz nach 15:00 die &#039;dayahead&#039; Preise liest.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40450</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40450"/>
		<updated>2025-10-19T10:18:17Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* EPEX_Spot  RAW Device */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
Diese Wiki Seite ist noch in Bearbeitung!!!&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgrufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen bekommen nur Kunden&#039;&#039;&#039;, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht untertsützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen fix Kosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute (&#039;EPEX_prices_day&#039;), für morgen (&#039;EPEX_prices_dayahead&#039;) und den aktuellen Preis (&#039;EPEX_price&#039;). &lt;br /&gt;
Die dayahead Preise werden in der Regel gegen 14:00 am Vortag veröffentlicht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD https://api.energy-charts.info/price?bzn=DE-LUtimerange 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00:30&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
The quarter-hour day and dayahead prices are given as lists of 96 elements each, the first price element being valid for the first quarter-hour 00:00 to 00:15, and so on.\&lt;br /&gt;
All prices are in €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise get02JSON price&lt;br /&gt;
attr EPEX_Boersenpreise get02Name EPEX_prices_day&lt;br /&gt;
attr EPEX_Boersenpreise get02Poll 1&lt;br /&gt;
attr EPEX_Boersenpreise get02RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Boersenpreise get02Replacement01Value {&amp;quot;&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise get03JSON price&lt;br /&gt;
attr EPEX_Boersenpreise get03Name EPEX_prices_dayahead&lt;br /&gt;
attr EPEX_Boersenpreise get03Poll 1&lt;br /&gt;
attr EPEX_Boersenpreise get03RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Boersenpreise get03Replacement01Value {fhem(&amp;quot;setreading $name EPEX_prices_dayahead not yet published&amp;quot;);;;;my ($sec, $min, $hour, $mday, $month, $year) = localtime();;;;my $start = 86400+int(fhemTimeLocal(0,0,0, $mday, $month, $year));;;;&amp;quot;&amp;amp;start=&amp;quot;.$start.&amp;quot;&amp;amp;end=&amp;quot;.($start+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise reading01JSON price&lt;br /&gt;
attr EPEX_Boersenpreise reading01Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Regex timerange&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Value {my ($sec, $min, $hour, $mday, $month, $year) = localtime();;;;&amp;quot;&amp;amp;start=&amp;quot;.900 * (int(fhemTimeLocal(0, $min, $hour, $mday, $month, $year) / 900))}&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise verbose 2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40449</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40449"/>
		<updated>2025-10-19T10:16:35Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* 3. EPEX_Spot */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
Diese Wiki Seite ist noch in Bearbeitung!!!&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgrufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen bekommen nur Kunden&#039;&#039;&#039;, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht untertsützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen fix Kosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute (&#039;EPEX_prices_day&#039;), für morgen (&#039;EPEX_prices_dayahead&#039;) und den aktuellen Preis (&#039;EPEX_price&#039;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD https://api.energy-charts.info/price?bzn=DE-LUtimerange 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00:30&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
The quarter-hour day and dayahead prices are given as lists of 96 elements each, the first price element being valid for the first quarter-hour 00:00 to 00:15, and so on.\&lt;br /&gt;
All prices are in €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise get02JSON price&lt;br /&gt;
attr EPEX_Boersenpreise get02Name EPEX_prices_day&lt;br /&gt;
attr EPEX_Boersenpreise get02Poll 1&lt;br /&gt;
attr EPEX_Boersenpreise get02RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Boersenpreise get02Replacement01Value {&amp;quot;&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise get03JSON price&lt;br /&gt;
attr EPEX_Boersenpreise get03Name EPEX_prices_dayahead&lt;br /&gt;
attr EPEX_Boersenpreise get03Poll 1&lt;br /&gt;
attr EPEX_Boersenpreise get03RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Boersenpreise get03Replacement01Value {fhem(&amp;quot;setreading $name EPEX_prices_dayahead not yet published&amp;quot;);;;;my ($sec, $min, $hour, $mday, $month, $year) = localtime();;;;my $start = 86400+int(fhemTimeLocal(0,0,0, $mday, $month, $year));;;;&amp;quot;&amp;amp;start=&amp;quot;.$start.&amp;quot;&amp;amp;end=&amp;quot;.($start+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise reading01JSON price&lt;br /&gt;
attr EPEX_Boersenpreise reading01Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Regex timerange&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Value {my ($sec, $min, $hour, $mday, $month, $year) = localtime();;;;&amp;quot;&amp;amp;start=&amp;quot;.900 * (int(fhemTimeLocal(0, $min, $hour, $mday, $month, $year) / 900))}&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise verbose 2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40448</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40448"/>
		<updated>2025-10-19T10:15:59Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
Diese Wiki Seite ist noch in Bearbeitung!!!&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgrufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen bekommen nur Kunden&#039;&#039;&#039;, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht untertsützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen fix Kosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
Das Device ist noch experimentell.&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit diesem device erhält man die viertelstündlichen Börsenpreise für heute (&#039;EPEX_prices_day&#039;), für morgen (&#039;EPEX_prices_dayahead&#039;) und den aktuellen Preis (&#039;EPEX_price&#039;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EPEX_Boersenpreise HTTPMOD https://api.energy-charts.info/price?bzn=DE-LUtimerange 900&lt;br /&gt;
attr EPEX_Boersenpreise alignTime 00:00:30&lt;br /&gt;
attr EPEX_Boersenpreise comment This device queries prices for electrical energy from https://api.energy-charts.info/price?bzn=DE-LU.\&lt;br /&gt;
The quarter-hour day and dayahead prices are given as lists of 96 elements each, the first price element being valid for the first quarter-hour 00:00 to 00:15, and so on.\&lt;br /&gt;
All prices are in €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise get02JSON price&lt;br /&gt;
attr EPEX_Boersenpreise get02Name EPEX_prices_day&lt;br /&gt;
attr EPEX_Boersenpreise get02Poll 1&lt;br /&gt;
attr EPEX_Boersenpreise get02RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Boersenpreise get02Replacement01Value {&amp;quot;&amp;quot;}&lt;br /&gt;
attr EPEX_Boersenpreise get03JSON price&lt;br /&gt;
attr EPEX_Boersenpreise get03Name EPEX_prices_dayahead&lt;br /&gt;
attr EPEX_Boersenpreise get03Poll 1&lt;br /&gt;
attr EPEX_Boersenpreise get03RecombineExpr join &amp;quot;,&amp;quot;, @matchlist&lt;br /&gt;
attr EPEX_Boersenpreise get03Replacement01Value {fhem(&amp;quot;setreading $name EPEX_prices_dayahead not yet published&amp;quot;);;;;my ($sec, $min, $hour, $mday, $month, $year) = localtime();;;;my $start = 86400+int(fhemTimeLocal(0,0,0, $mday, $month, $year));;;;&amp;quot;&amp;amp;start=&amp;quot;.$start.&amp;quot;&amp;amp;end=&amp;quot;.($start+86399)}&lt;br /&gt;
attr EPEX_Boersenpreise reading01JSON price&lt;br /&gt;
attr EPEX_Boersenpreise reading01Name EPEX_price&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Mode expression&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Regex timerange&lt;br /&gt;
attr EPEX_Boersenpreise replacement01Value {my ($sec, $min, $hour, $mday, $month, $year) = localtime();;;;&amp;quot;&amp;amp;start=&amp;quot;.900 * (int(fhemTimeLocal(0, $min, $hour, $mday, $month, $year) / 900))}&lt;br /&gt;
attr EPEX_Boersenpreise stateFormat EPEX_price €/MWh&lt;br /&gt;
attr EPEX_Boersenpreise verbose 2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40445</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40445"/>
		<updated>2025-10-18T02:52:16Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: Änderung 40444 von Alkazaa (Diskussion) rückgängig gemacht.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
Diese Wiki Seite ist noch in Bearbeitung!!!&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgrufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen bekommen nur Kunden&#039;&#039;&#039;, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht untertsützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen fix Kosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
Das Device ist noch experimentell.&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit zwei set kann man &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EPEX_Spot HTTPMOD none&lt;br /&gt;
attr EPEX_Spot comment https://api.energy-charts.info\&lt;br /&gt;
https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=2025-06-25&amp;amp;end=2025-06-25\&lt;br /&gt;
\&lt;br /&gt;
https://api.energy-charts.info/price?bzn=DE-LU 3600&lt;br /&gt;
attr EPEX_Spot disable 0&lt;br /&gt;
attr EPEX_Spot extractAllJSON 0&lt;br /&gt;
attr EPEX_Spot get01-0Name fc0_00_total&lt;br /&gt;
attr EPEX_Spot get01-0OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-10Name fc0_09&lt;br /&gt;
attr EPEX_Spot get01-10OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-11Name fc0_10&lt;br /&gt;
attr EPEX_Spot get01-11OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-12Name fc0_11&lt;br /&gt;
attr EPEX_Spot get01-12OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-13Name fc0_12&lt;br /&gt;
attr EPEX_Spot get01-13OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-14Name fc0_13&lt;br /&gt;
attr EPEX_Spot get01-14OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-15Name fc0_14&lt;br /&gt;
attr EPEX_Spot get01-15OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-16Name fc0_15&lt;br /&gt;
attr EPEX_Spot get01-16OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-17Name fc0_16&lt;br /&gt;
attr EPEX_Spot get01-17OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-18Name fc0_17&lt;br /&gt;
attr EPEX_Spot get01-18OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-19Name fc0_18&lt;br /&gt;
attr EPEX_Spot get01-19OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-1Name fc0_00&lt;br /&gt;
attr EPEX_Spot get01-1OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-20Name fc0_19&lt;br /&gt;
attr EPEX_Spot get01-20OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-21Name fc0_20&lt;br /&gt;
attr EPEX_Spot get01-21OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-22Name fc0_21&lt;br /&gt;
attr EPEX_Spot get01-22OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-23Name fc0_22&lt;br /&gt;
attr EPEX_Spot get01-23OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-24Name fc0_23&lt;br /&gt;
attr EPEX_Spot get01-24OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-2Name fc0_01&lt;br /&gt;
attr EPEX_Spot get01-2OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-3Name fc0_02&lt;br /&gt;
attr EPEX_Spot get01-3OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-4Name fc0_03&lt;br /&gt;
attr EPEX_Spot get01-4OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-5Name fc0_04&lt;br /&gt;
attr EPEX_Spot get01-5OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-6Name fc0_05&lt;br /&gt;
attr EPEX_Spot get01-6OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-7Name fc0_06&lt;br /&gt;
attr EPEX_Spot get01-7OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-8Name fc0_07&lt;br /&gt;
attr EPEX_Spot get01-8OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-9Name fc0_08&lt;br /&gt;
attr EPEX_Spot get01-9OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01Name 01_priceinfo_today&lt;br /&gt;
attr EPEX_Spot get01RegOpt g&lt;br /&gt;
attr EPEX_Spot get01Regex [price&amp;quot;:\[|,](-?\d+\.\d+)&lt;br /&gt;
attr EPEX_Spot get01URL https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=%%start_today%%&lt;br /&gt;
attr EPEX_Spot get02-0Name fc1_00_total&lt;br /&gt;
attr EPEX_Spot get02-0OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-10Name fc1_09&lt;br /&gt;
attr EPEX_Spot get02-10OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-11Name fc1_10&lt;br /&gt;
attr EPEX_Spot get02-11OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-12Name fc1_11&lt;br /&gt;
attr EPEX_Spot get02-12OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-13Name fc1_12&lt;br /&gt;
attr EPEX_Spot get02-13OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-14Name fc1_13&lt;br /&gt;
attr EPEX_Spot get02-14OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-15Name fc1_14&lt;br /&gt;
attr EPEX_Spot get02-15OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-16Name fc1_15&lt;br /&gt;
attr EPEX_Spot get02-16OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-17Name fc1_16&lt;br /&gt;
attr EPEX_Spot get02-17OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-18Name fc1_17&lt;br /&gt;
attr EPEX_Spot get02-18OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-19Name fc1_18&lt;br /&gt;
attr EPEX_Spot get02-19OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-1Name fc1_00&lt;br /&gt;
attr EPEX_Spot get02-1OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-20Name fc1_19&lt;br /&gt;
attr EPEX_Spot get02-20OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-21Name fc1_20&lt;br /&gt;
attr EPEX_Spot get02-21OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-22Name fc1_21&lt;br /&gt;
attr EPEX_Spot get02-22OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-23Name fc1_22&lt;br /&gt;
attr EPEX_Spot get02-23OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-24Name fc1_23&lt;br /&gt;
attr EPEX_Spot get02-24OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-2Name fc1_01&lt;br /&gt;
attr EPEX_Spot get02-2OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-3Name fc1_02&lt;br /&gt;
attr EPEX_Spot get02-3OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-4Name fc1_03&lt;br /&gt;
attr EPEX_Spot get02-4OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-5Name fc1_04&lt;br /&gt;
attr EPEX_Spot get02-5OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-6Name fc1_05&lt;br /&gt;
attr EPEX_Spot get02-6OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-7Name fc1_06&lt;br /&gt;
attr EPEX_Spot get02-7OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-8Name fc1_07&lt;br /&gt;
attr EPEX_Spot get02-8OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-9Name fc1_08&lt;br /&gt;
attr EPEX_Spot get02-9OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02Name 02_priceinfo_tomorrow&lt;br /&gt;
attr EPEX_Spot get02RegOpt g&lt;br /&gt;
attr EPEX_Spot get02Regex [price&amp;quot;:\[|,](-?\d+\.\d+)&lt;br /&gt;
attr EPEX_Spot get02URL https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=%%start_tomorrow%%&lt;br /&gt;
attr EPEX_Spot group price&lt;br /&gt;
attr EPEX_Spot icon stromzaehler_icon&lt;br /&gt;
attr EPEX_Spot replacement01Mode expression&lt;br /&gt;
attr EPEX_Spot replacement01Regex %%start_today%%&lt;br /&gt;
attr EPEX_Spot replacement01Value strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time))&lt;br /&gt;
attr EPEX_Spot replacement02Mode expression&lt;br /&gt;
attr EPEX_Spot replacement02Regex %%start_tomorrow%%&lt;br /&gt;
attr EPEX_Spot replacement02Value strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time()+86400))&lt;br /&gt;
attr EPEX_Spot room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EPEX_Spot showBody 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40444</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=40444"/>
		<updated>2025-10-18T02:41:53Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=EPEX_Spot}}&lt;br /&gt;
&lt;br /&gt;
Diese Wiki Seite ist noch in Bearbeitung!!!&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; 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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgrufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
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.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen bekommen nur Kunden&#039;&#039;&#039;, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
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.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;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;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht untertsützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen fix Kosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
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\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
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.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. EPEX_Spot ==&lt;br /&gt;
Das Device ist noch experimentell.&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
httpmod&lt;br /&gt;
&lt;br /&gt;
==== EPEX_Spot  RAW Device ====&lt;br /&gt;
Mit zwei set kann man &lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot; style=&amp;quot;border:1px solid #aaa; padding:0.5em;&amp;quot;&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Beispielcode (ausklappen)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EPEX_Spot HTTPMOD none&lt;br /&gt;
attr EPEX_Spot comment https://api.energy-charts.info\&lt;br /&gt;
https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=2025-06-25&amp;amp;end=2025-06-25\&lt;br /&gt;
\&lt;br /&gt;
https://api.energy-charts.info/price?bzn=DE-LU 3600&lt;br /&gt;
attr EPEX_Spot disable 0&lt;br /&gt;
attr EPEX_Spot extractAllJSON 0&lt;br /&gt;
attr EPEX_Spot get01-0Name fc0_00_total&lt;br /&gt;
attr EPEX_Spot get01-0OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-10Name fc0_09&lt;br /&gt;
attr EPEX_Spot get01-10OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-11Name fc0_10&lt;br /&gt;
attr EPEX_Spot get01-11OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-12Name fc0_11&lt;br /&gt;
attr EPEX_Spot get01-12OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-13Name fc0_12&lt;br /&gt;
attr EPEX_Spot get01-13OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-14Name fc0_13&lt;br /&gt;
attr EPEX_Spot get01-14OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-15Name fc0_14&lt;br /&gt;
attr EPEX_Spot get01-15OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-16Name fc0_15&lt;br /&gt;
attr EPEX_Spot get01-16OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-17Name fc0_16&lt;br /&gt;
attr EPEX_Spot get01-17OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-18Name fc0_17&lt;br /&gt;
attr EPEX_Spot get01-18OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-19Name fc0_18&lt;br /&gt;
attr EPEX_Spot get01-19OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-1Name fc0_00&lt;br /&gt;
attr EPEX_Spot get01-1OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-20Name fc0_19&lt;br /&gt;
attr EPEX_Spot get01-20OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-21Name fc0_20&lt;br /&gt;
attr EPEX_Spot get01-21OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-22Name fc0_21&lt;br /&gt;
attr EPEX_Spot get01-22OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-23Name fc0_22&lt;br /&gt;
attr EPEX_Spot get01-23OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-24Name fc0_23&lt;br /&gt;
attr EPEX_Spot get01-24OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-2Name fc0_01&lt;br /&gt;
attr EPEX_Spot get01-2OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-3Name fc0_02&lt;br /&gt;
attr EPEX_Spot get01-3OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-4Name fc0_03&lt;br /&gt;
attr EPEX_Spot get01-4OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-5Name fc0_04&lt;br /&gt;
attr EPEX_Spot get01-5OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-6Name fc0_05&lt;br /&gt;
attr EPEX_Spot get01-6OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-7Name fc0_06&lt;br /&gt;
attr EPEX_Spot get01-7OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-8Name fc0_07&lt;br /&gt;
attr EPEX_Spot get01-8OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01-9Name fc0_08&lt;br /&gt;
attr EPEX_Spot get01-9OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get01Name 01_priceinfo_today&lt;br /&gt;
attr EPEX_Spot get01RegOpt g&lt;br /&gt;
attr EPEX_Spot get01Regex [price&amp;quot;:\[|,](-?\d+\.\d+)&lt;br /&gt;
attr EPEX_Spot get01URL https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=%%start_today%%&lt;br /&gt;
attr EPEX_Spot get02-0Name fc1_00_total&lt;br /&gt;
attr EPEX_Spot get02-0OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-10Name fc1_09&lt;br /&gt;
attr EPEX_Spot get02-10OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-11Name fc1_10&lt;br /&gt;
attr EPEX_Spot get02-11OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-12Name fc1_11&lt;br /&gt;
attr EPEX_Spot get02-12OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-13Name fc1_12&lt;br /&gt;
attr EPEX_Spot get02-13OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-14Name fc1_13&lt;br /&gt;
attr EPEX_Spot get02-14OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-15Name fc1_14&lt;br /&gt;
attr EPEX_Spot get02-15OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-16Name fc1_15&lt;br /&gt;
attr EPEX_Spot get02-16OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-17Name fc1_16&lt;br /&gt;
attr EPEX_Spot get02-17OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-18Name fc1_17&lt;br /&gt;
attr EPEX_Spot get02-18OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-19Name fc1_18&lt;br /&gt;
attr EPEX_Spot get02-19OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-1Name fc1_00&lt;br /&gt;
attr EPEX_Spot get02-1OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-20Name fc1_19&lt;br /&gt;
attr EPEX_Spot get02-20OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-21Name fc1_20&lt;br /&gt;
attr EPEX_Spot get02-21OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-22Name fc1_21&lt;br /&gt;
attr EPEX_Spot get02-22OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-23Name fc1_22&lt;br /&gt;
attr EPEX_Spot get02-23OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-24Name fc1_23&lt;br /&gt;
attr EPEX_Spot get02-24OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-2Name fc1_01&lt;br /&gt;
attr EPEX_Spot get02-2OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-3Name fc1_02&lt;br /&gt;
attr EPEX_Spot get02-3OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-4Name fc1_03&lt;br /&gt;
attr EPEX_Spot get02-4OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-5Name fc1_04&lt;br /&gt;
attr EPEX_Spot get02-5OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-6Name fc1_05&lt;br /&gt;
attr EPEX_Spot get02-6OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-7Name fc1_06&lt;br /&gt;
attr EPEX_Spot get02-7OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-8Name fc1_07&lt;br /&gt;
attr EPEX_Spot get02-8OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02-9Name fc1_08&lt;br /&gt;
attr EPEX_Spot get02-9OExpr round($val,2)&lt;br /&gt;
attr EPEX_Spot get02Name 02_priceinfo_tomorrow&lt;br /&gt;
attr EPEX_Spot get02RegOpt g&lt;br /&gt;
attr EPEX_Spot get02Regex [price&amp;quot;:\[|,](-?\d+\.\d+)&lt;br /&gt;
attr EPEX_Spot get02URL https://api.energy-charts.info/price?bzn=DE-LU&amp;amp;start=%%start_tomorrow%%&lt;br /&gt;
attr EPEX_Spot group price&lt;br /&gt;
attr EPEX_Spot icon stromzaehler_icon&lt;br /&gt;
attr EPEX_Spot replacement01Mode expression&lt;br /&gt;
attr EPEX_Spot replacement01Regex %%start_today%%&lt;br /&gt;
attr EPEX_Spot replacement01Value strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time))&lt;br /&gt;
attr EPEX_Spot replacement02Mode expression&lt;br /&gt;
attr EPEX_Spot replacement02Regex %%start_tomorrow%%&lt;br /&gt;
attr EPEX_Spot replacement02Value strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time()+86400))&lt;br /&gt;
attr EPEX_Spot room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EPEX_Spot showBody 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=CDCOpenData&amp;diff=38716</id>
		<title>CDCOpenData</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=CDCOpenData&amp;diff=38716"/>
		<updated>2023-11-10T20:34:06Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Abruf zuletzt gefallener und kurzfristig vorhergesagter Regenmengen für vorgegebene Orte.&lt;br /&gt;
Bitte nach &amp;quot;Anleitung&amp;quot; [[Vorlage:Infobox_Modul]] vervollständigen&lt;br /&gt;
|ModType=x&lt;br /&gt;
|ModCmdRef= &lt;br /&gt;
|ModForumArea=&lt;br /&gt;
|ModFTopic=&lt;br /&gt;
|ModTechName=&lt;br /&gt;
|ModOwner=&lt;br /&gt;
}}&lt;br /&gt;
&#039;&#039;&#039;Diese Seite ist noch im Aufbau&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der DWD stellt auf den [https://opendata.dwd.de/climate_environment/CDC/grids_germany/daily/radolan/ ftp-Seiten seines Climate Data Centers (CDC)] Werte der pro Tag gefallenen Regenmengen zur Verfügung, die auf Regenradar-Messungen beruhen und deren Werte an die gemessenen Mengen der Wetterstation angeeicht wurden. Die räumliche Auflösung beträgt dabei 1 km, was die Daten für diejenigen interessant macht, die keine eigene Regenmengenmessung zur Verfügung haben.&lt;br /&gt;
&lt;br /&gt;
Mit der gleichen räumlichen Auflösung sind auch Daten der vorhergesagten Niederschläge der nächsten 120 Minuten verfügbar. Es sind die gleichen Daten, die z.B. auch im Regenradarfilm der [https://www.dwd.de/DE/leistungen/warnwetterapp/warnwetterapp.html DWD WarnWetter-App] als Film dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
Mit dem Modul 98_CDCOpenData.pm kann auf diese Daten zugegriffen werden. Zur Zeit liefert das Modul folgende Daten:&lt;br /&gt;
&lt;br /&gt;
* die Regenmenge der letzten N Tage (N konfigurierbar, default 5); update beim DWD täglich&lt;br /&gt;
* die Regenmenge seit Mitternacht; update stündlich&lt;br /&gt;
* die prognostizierten Regenintensitäten der nächsten 120 Minuten; update alle 5 Minuten&lt;br /&gt;
&lt;br /&gt;
Die Werte werden für den akuellen &#039;Home&#039;-Ort (soweit in global per latitude/longitude definiert) und für weitere konfigurierbare Orte in Deutschland und einigen angrenzenden Bereichen von Nachbarländern geliefert.&lt;br /&gt;
&lt;br /&gt;
==Anwendung==&lt;br /&gt;
Aufgrund der unterschiedlichen vom DWD bereitgestellten Datenformate benötigt das Modul zur Dekodierung u,U, zusätzliche Perl-Module, die nicht standardmäßig vorinstalliert sind und je nach Systemkonfiguration nachinstalliert werden müssen. &lt;br /&gt;
&lt;br /&gt;
===Vorbereitung ===&lt;br /&gt;
Da Modul wird über den regulären FHEM-update Prozess bereitgestellt&lt;br /&gt;
&lt;br /&gt;
===Define ===&lt;br /&gt;
&amp;lt;code&amp;gt;define &amp;lt;devicename&amp;gt; CDCOpenData&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;devicename&amp;gt; enableDWDdata &amp;lt;rainByDay, rainSinceMidnight, rainRadarbyLocation&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wählt aus, welche Daten periodisch abgefragt werden. Standardmäßig ist nichts ausgewählt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;devicename&amp;gt; INTERVAL 300&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Legt das Abfrage-Intervall fest. Der Standardwert ist 300 (Sekunden). Kleinster möglicher Wert ist 60. Wenn das Attribut cronTime gesetzt ist, wird INTERVAL deaktiviert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;devicename&amp;gt; cronTime &amp;lt;* * * * *&amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Falls gesetzt, bestimmt der cron-Ausdruck das Abfrageintervall. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;devicename&amp;gt; locations &amp;lt;[name:]latitude,longitude&amp;gt; [name:]latitude,longitude ...&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Durch Leerzeichen getrennte Liste weiterer Orte, für die Daten zusätzlich zum Standard-Ort abgefragt werden. &amp;lt;name[:]&amp;gt; ist optional der &amp;quot;sprechende&amp;quot; Name des Ortes. &lt;br /&gt;
&lt;br /&gt;
==Anwendungsbeispiele==&lt;br /&gt;
===Beispiel zur Modul-Einrichtung===&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=CDCOpenData&amp;diff=38715</id>
		<title>CDCOpenData</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=CDCOpenData&amp;diff=38715"/>
		<updated>2023-11-10T20:31:47Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Mit dem Modul können die zuletzt gefallenen und die kurzfristig vorhergesagten Regenmengen für vorgegebene Orte von DWD Seiten abgerufen werden.&lt;br /&gt;
Bitte nach &amp;quot;Anleitung&amp;quot; [[Vorlage:Infobox_Modul]] vervollständigen&lt;br /&gt;
|ModType=x&lt;br /&gt;
|ModCmdRef= &lt;br /&gt;
|ModForumArea=&lt;br /&gt;
|ModFTopic=&lt;br /&gt;
|ModTechName=&lt;br /&gt;
|ModOwner=&lt;br /&gt;
}}&lt;br /&gt;
&#039;&#039;&#039;Diese Seite ist noch im Aufbau&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der DWD stellt auf den [https://opendata.dwd.de/climate_environment/CDC/grids_germany/daily/radolan/ ftp-Seiten seines Climate Data Centers (CDC)] Werte der pro Tag gefallenen Regenmengen zur Verfügung, die auf Regenradar-Messungen beruhen und deren Werte an die gemessenen Mengen der Wetterstation angeeicht wurden. Die räumliche Auflösung beträgt dabei 1 km, was die Daten für diejenigen interessant macht, die keine eigene Regenmengenmessung zur Verfügung haben.&lt;br /&gt;
&lt;br /&gt;
Mit der gleichen räumlichen Auflösung sind auch Daten der vorhergesagten Niederschläge der nächsten 120 Minuten verfügbar. Es sind die gleichen Daten, die z.B. auch im Regenradarfilm der [https://www.dwd.de/DE/leistungen/warnwetterapp/warnwetterapp.html DWD WarnWetter-App] als Film dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
Mit dem Modul 98_CDCOpenData.pm kann auf diese Daten zugegriffen werden. Zur Zeit liefert das Modul folgende Daten:&lt;br /&gt;
&lt;br /&gt;
* die Regenmenge der letzten N Tage (N konfigurierbar, default 5); update beim DWD täglich&lt;br /&gt;
* die Regenmenge seit Mitternacht; update stündlich&lt;br /&gt;
* die prognostizierten Regenintensitäten der nächsten 120 Minuten; update alle 5 Minuten&lt;br /&gt;
&lt;br /&gt;
Die Werte werden für den akuellen &#039;Home&#039;-Ort (soweit in global per latitude/longitude definiert) und für weitere konfigurierbare Orte in Deutschland und einigen angrenzenden Bereichen von Nachbarländern geliefert.&lt;br /&gt;
&lt;br /&gt;
==Anwendung==&lt;br /&gt;
Aufgrund der unterschiedlichen vom DWD bereitgestellten Datenformate benötigt das Modul zur Dekodierung u,U, zusätzliche Perl-Module, die nicht standardmäßig vorinstalliert sind und je nach Systemkonfiguration nachinstalliert werden müssen. &lt;br /&gt;
&lt;br /&gt;
===Vorbereitung ===&lt;br /&gt;
Da Modul wird über den regulären FHEM-update Prozess bereitgestellt&lt;br /&gt;
&lt;br /&gt;
===Define ===&lt;br /&gt;
&amp;lt;code&amp;gt;define &amp;lt;devicename&amp;gt; CDCOpenData&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;devicename&amp;gt; enableDWDdata &amp;lt;rainByDay, rainSinceMidnight, rainRadarbyLocation&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wählt aus, welche Daten periodisch abgefragt werden. Standardmäßig ist nichts ausgewählt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;devicename&amp;gt; INTERVAL 300&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Legt das Abfrage-Intervall fest. Der Standardwert ist 300 (Sekunden). Kleinster möglicher Wert ist 60. Wenn das Attribut cronTime gesetzt ist, wird INTERVAL deaktiviert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;devicename&amp;gt; cronTime &amp;lt;* * * * *&amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Falls gesetzt, bestimmt der cron-Ausdruck das Abfrageintervall. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;devicename&amp;gt; locations &amp;lt;[name:]latitude,longitude&amp;gt; [name:]latitude,longitude ...&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Durch Leerzeichen getrennte Liste weiterer Orte, für die Daten zusätzlich zum Standard-Ort abgefragt werden. &amp;lt;name[:]&amp;gt; ist optional der &amp;quot;sprechende&amp;quot; Name des Ortes. &lt;br /&gt;
&lt;br /&gt;
==Anwendungsbeispiele==&lt;br /&gt;
===Beispiel zur Modul-Einrichtung===&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=CDCOpenData&amp;diff=38714</id>
		<title>CDCOpenData</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=CDCOpenData&amp;diff=38714"/>
		<updated>2023-11-10T20:23:15Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* Attribute */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Bitte nach &amp;quot;Anleitung&amp;quot; [[Vorlage:Infobox_Modul]] vervollständigen&lt;br /&gt;
|ModType=x&lt;br /&gt;
|ModCmdRef= &lt;br /&gt;
|ModForumArea=&lt;br /&gt;
|ModFTopic=&lt;br /&gt;
|ModTechName=&lt;br /&gt;
|ModOwner=&lt;br /&gt;
}}&lt;br /&gt;
&#039;&#039;&#039;Diese Seite ist noch im Aufbau&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der DWD stellt auf den [https://opendata.dwd.de/climate_environment/CDC/grids_germany/daily/radolan/ ftp-Seiten seines Climate Data Centers (CDC)] Werte der pro Tag gefallenen Regenmengen zur Verfügung, die auf Regenradar-Messungen beruhen und deren Werte an die gemessenen Mengen der Wetterstation angeeicht wurden. Die räumliche Auflösung beträgt dabei 1 km, was die Daten für diejenigen interessant macht, die keine eigene Regenmengenmessung zur Verfügung haben.&lt;br /&gt;
&lt;br /&gt;
Mit der gleichen räumlichen Auflösung sind auch Daten der vorhergesagten Niederschläge der nächsten 120 Minuten verfügbar. Es sind die gleichen Daten, die z.B. auch im Regenradarfilm der [https://www.dwd.de/DE/leistungen/warnwetterapp/warnwetterapp.html DWD WarnWetter-App] als Film dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
Mit dem Modul 98_CDCOpenData.pm kann auf diese Daten zugegriffen werden. Zur Zeit liefert das Modul folgende Daten:&lt;br /&gt;
&lt;br /&gt;
* die Regenmenge der letzten N Tage (N konfigurierbar, default 5); update beim DWD täglich&lt;br /&gt;
* die Regenmenge seit Mitternacht; update stündlich&lt;br /&gt;
* die prognostizierten Regenintensitäten der nächsten 120 Minuten; update alle 5 Minuten&lt;br /&gt;
&lt;br /&gt;
Die Werte werden für den akuellen &#039;Home&#039;-Ort (soweit in global per latitude/longitude definiert) und für weitere konfigurierbare Orte in Deutschland und einigen angrenzenden Bereichen von Nachbarländern geliefert.&lt;br /&gt;
&lt;br /&gt;
==Anwendung==&lt;br /&gt;
Aufgrund der unterschiedlichen vom DWD bereitgestellten Datenformate benötigt das Modul zur Dekodierung u,U, zusätzliche Perl-Module, die nicht standardmäßig vorinstalliert sind und je nach Systemkonfiguration nachinstalliert werden müssen. &lt;br /&gt;
&lt;br /&gt;
===Vorbereitung ===&lt;br /&gt;
Da Modul wird über den regulären FHEM-update Prozess bereitgestellt&lt;br /&gt;
&lt;br /&gt;
===Define ===&lt;br /&gt;
&amp;lt;code&amp;gt;define &amp;lt;devicename&amp;gt; CDCOpenData&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;devicename&amp;gt; enableDWDdata &amp;lt;rainByDay, rainSinceMidnight, rainRadarbyLocation&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wählt aus, welche Daten periodisch abgefragt werden. Standardmäßig ist nichts ausgewählt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;devicename&amp;gt; INTERVAL 300&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Legt das Abfrage-Intervall fest. Der Standardwert ist 300 (Sekunden). Kleinster möglicher Wert ist 60. Wenn das Attribut cronTime gesetzt ist, wird INTERVAL deaktiviert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;devicename&amp;gt; cronTime &amp;lt;* * * * *&amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Falls gesetzt, bestimmt der cron-Ausdruck das Abfrageintervall. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;devicename&amp;gt; locations &amp;lt;[name:]latitude,longitude&amp;gt; [name:]latitude,longitude ...&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Durch Leerzeichen getrennte Liste weiterer Orte, für die Daten zusätzlich zum Standard-Ort abgefragt werden. &amp;lt;name[:]&amp;gt; ist optional der &amp;quot;sprechende&amp;quot; Name des Ortes. &lt;br /&gt;
&lt;br /&gt;
==Anwendungsbeispiele==&lt;br /&gt;
===Beispiel zur Modul-Einrichtung===&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=CDCOpenData&amp;diff=38713</id>
		<title>CDCOpenData</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=CDCOpenData&amp;diff=38713"/>
		<updated>2023-11-10T20:22:44Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* Attribute */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Bitte nach &amp;quot;Anleitung&amp;quot; [[Vorlage:Infobox_Modul]] vervollständigen&lt;br /&gt;
|ModType=x&lt;br /&gt;
|ModCmdRef= &lt;br /&gt;
|ModForumArea=&lt;br /&gt;
|ModFTopic=&lt;br /&gt;
|ModTechName=&lt;br /&gt;
|ModOwner=&lt;br /&gt;
}}&lt;br /&gt;
&#039;&#039;&#039;Diese Seite ist noch im Aufbau&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der DWD stellt auf den [https://opendata.dwd.de/climate_environment/CDC/grids_germany/daily/radolan/ ftp-Seiten seines Climate Data Centers (CDC)] Werte der pro Tag gefallenen Regenmengen zur Verfügung, die auf Regenradar-Messungen beruhen und deren Werte an die gemessenen Mengen der Wetterstation angeeicht wurden. Die räumliche Auflösung beträgt dabei 1 km, was die Daten für diejenigen interessant macht, die keine eigene Regenmengenmessung zur Verfügung haben.&lt;br /&gt;
&lt;br /&gt;
Mit der gleichen räumlichen Auflösung sind auch Daten der vorhergesagten Niederschläge der nächsten 120 Minuten verfügbar. Es sind die gleichen Daten, die z.B. auch im Regenradarfilm der [https://www.dwd.de/DE/leistungen/warnwetterapp/warnwetterapp.html DWD WarnWetter-App] als Film dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
Mit dem Modul 98_CDCOpenData.pm kann auf diese Daten zugegriffen werden. Zur Zeit liefert das Modul folgende Daten:&lt;br /&gt;
&lt;br /&gt;
* die Regenmenge der letzten N Tage (N konfigurierbar, default 5); update beim DWD täglich&lt;br /&gt;
* die Regenmenge seit Mitternacht; update stündlich&lt;br /&gt;
* die prognostizierten Regenintensitäten der nächsten 120 Minuten; update alle 5 Minuten&lt;br /&gt;
&lt;br /&gt;
Die Werte werden für den akuellen &#039;Home&#039;-Ort (soweit in global per latitude/longitude definiert) und für weitere konfigurierbare Orte in Deutschland und einigen angrenzenden Bereichen von Nachbarländern geliefert.&lt;br /&gt;
&lt;br /&gt;
==Anwendung==&lt;br /&gt;
Aufgrund der unterschiedlichen vom DWD bereitgestellten Datenformate benötigt das Modul zur Dekodierung u,U, zusätzliche Perl-Module, die nicht standardmäßig vorinstalliert sind und je nach Systemkonfiguration nachinstalliert werden müssen. &lt;br /&gt;
&lt;br /&gt;
===Vorbereitung ===&lt;br /&gt;
Da Modul wird über den regulären FHEM-update Prozess bereitgestellt&lt;br /&gt;
&lt;br /&gt;
===Define ===&lt;br /&gt;
&amp;lt;code&amp;gt;define &amp;lt;devicename&amp;gt; CDCOpenData&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;devicename&amp;gt; enableDWDdata &amp;lt;rainByDay, rainSinceMidnight, rainRadarbyLocation&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wählt aus, welche Daten periodisch abgefragt werden. Standardmäßig ist nichts ausgewählt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;devicename&amp;gt; INTERVAL 300&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Legt das Abfrage-Intervall fest. Der Standardwert ist 300 (Sekunden). Kleinster möglicher Wert ist 60. Wenn das Attribut cronTime gesetzt ist, wird INTERVAL deaktiviert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;devicename&amp;gt; cronTime &amp;lt;* * * * *&amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Falls gesetzt, bestimmt der cron-Ausdruck das Abfrageintervall. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;devicename&amp;gt; locations &amp;lt;[name:]latitude,longitude&amp;gt; [name:]latitude,longitude ...&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Durch Leerzeichen getrennte Liste weiterer Orte, für die Daten zusätzlich zum Standard-Ort abgefragt werden. &amp;lt;name[:]&amp;gt; ist ist optional der &amp;quot;sprechende&amp;quot; Name des Ortes. &lt;br /&gt;
&lt;br /&gt;
==Anwendungsbeispiele==&lt;br /&gt;
===Beispiel zur Modul-Einrichtung===&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=CDCOpenData&amp;diff=38712</id>
		<title>CDCOpenData</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=CDCOpenData&amp;diff=38712"/>
		<updated>2023-11-10T20:22:10Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* Define */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Bitte nach &amp;quot;Anleitung&amp;quot; [[Vorlage:Infobox_Modul]] vervollständigen&lt;br /&gt;
|ModType=x&lt;br /&gt;
|ModCmdRef= &lt;br /&gt;
|ModForumArea=&lt;br /&gt;
|ModFTopic=&lt;br /&gt;
|ModTechName=&lt;br /&gt;
|ModOwner=&lt;br /&gt;
}}&lt;br /&gt;
&#039;&#039;&#039;Diese Seite ist noch im Aufbau&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der DWD stellt auf den [https://opendata.dwd.de/climate_environment/CDC/grids_germany/daily/radolan/ ftp-Seiten seines Climate Data Centers (CDC)] Werte der pro Tag gefallenen Regenmengen zur Verfügung, die auf Regenradar-Messungen beruhen und deren Werte an die gemessenen Mengen der Wetterstation angeeicht wurden. Die räumliche Auflösung beträgt dabei 1 km, was die Daten für diejenigen interessant macht, die keine eigene Regenmengenmessung zur Verfügung haben.&lt;br /&gt;
&lt;br /&gt;
Mit der gleichen räumlichen Auflösung sind auch Daten der vorhergesagten Niederschläge der nächsten 120 Minuten verfügbar. Es sind die gleichen Daten, die z.B. auch im Regenradarfilm der [https://www.dwd.de/DE/leistungen/warnwetterapp/warnwetterapp.html DWD WarnWetter-App] als Film dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
Mit dem Modul 98_CDCOpenData.pm kann auf diese Daten zugegriffen werden. Zur Zeit liefert das Modul folgende Daten:&lt;br /&gt;
&lt;br /&gt;
* die Regenmenge der letzten N Tage (N konfigurierbar, default 5); update beim DWD täglich&lt;br /&gt;
* die Regenmenge seit Mitternacht; update stündlich&lt;br /&gt;
* die prognostizierten Regenintensitäten der nächsten 120 Minuten; update alle 5 Minuten&lt;br /&gt;
&lt;br /&gt;
Die Werte werden für den akuellen &#039;Home&#039;-Ort (soweit in global per latitude/longitude definiert) und für weitere konfigurierbare Orte in Deutschland und einigen angrenzenden Bereichen von Nachbarländern geliefert.&lt;br /&gt;
&lt;br /&gt;
==Anwendung==&lt;br /&gt;
Aufgrund der unterschiedlichen vom DWD bereitgestellten Datenformate benötigt das Modul zur Dekodierung u,U, zusätzliche Perl-Module, die nicht standardmäßig vorinstalliert sind und je nach Systemkonfiguration nachinstalliert werden müssen. &lt;br /&gt;
&lt;br /&gt;
===Vorbereitung ===&lt;br /&gt;
Da Modul wird über den regulären FHEM-update Prozess bereitgestellt&lt;br /&gt;
&lt;br /&gt;
===Define ===&lt;br /&gt;
&amp;lt;code&amp;gt;define &amp;lt;devicename&amp;gt; CDCOpenData&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;name&amp;gt; enableDWDdata &amp;lt;rainByDay, rainSinceMidnight, rainRadarbyLocation&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wählt aus, welche Daten periodisch abgefragt werden. Standardmäßig ist nichts ausgewählt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;name&amp;gt; INTERVAL 300&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Legt das Abfrage-Intervall fest. Der Standardwert ist 300 (Sekunden). Kleinster möglicher Wert ist 60. Wenn das Attribut cronTime gesetzt ist, wird INTERVAL deaktiviert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;name&amp;gt; cronTime &amp;lt;* * * * *&amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Falls gesetzt, bestimmt der cron-Ausdruck das Abfrageintervall. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;name&amp;gt; locations &amp;lt;[name:]latitude,longitude&amp;gt; [name:]latitude,longitude ...&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Durch Leerzeichen getrennte Liste weiterer Orte, für die Daten zusätzlich zum Standard-Ort abgefragt werden. &amp;lt;name[:]&amp;gt; ist ist optional der &amp;quot;sprechende&amp;quot; Name des Ortes. &lt;br /&gt;
&lt;br /&gt;
==Anwendungsbeispiele==&lt;br /&gt;
===Beispiel zur Modul-Einrichtung===&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Wetter_und_Wettervorhersagen&amp;diff=38711</id>
		<title>Wetter und Wettervorhersagen</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Wetter_und_Wettervorhersagen&amp;diff=38711"/>
		<updated>2023-11-10T20:20:14Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* Modul CDCOpenData */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Wetterdaten und Wettervorhersagen können auf verschiedene Weise in FHEM eingebunden werden.&lt;br /&gt;
== Modul Weather ==&lt;br /&gt;
Das Modul [[Weather]] extrahiert Wetterdaten via API von den Wetterdatenanbietern&lt;br /&gt;
* [https://openweathermap.org/ Openweathermap]&lt;br /&gt;
** Vorschauzeitraum: 5 Tage (zeitliche Auflösung: 3 Stunden)&lt;br /&gt;
** Aktuelle Daten (minütlich)&lt;br /&gt;
** 1000 Abrufe pro Tag mit dem Gratis-API-Key (Stand: Juni 2021)&lt;br /&gt;
* [https://www.wunderground.com/ wunderground]&lt;br /&gt;
** API-KEY nur für Gerätebesitzer&lt;br /&gt;
* [https://darksky.net/ DarkSky]&lt;br /&gt;
** Support for the Dark Sky API ended on March 31, 2023, and has been replaced by Apple’s WeatherKit API. &lt;br /&gt;
** Es können keine neuen API-Key erstellt werden (Stand: Juni 2021)&lt;br /&gt;
&lt;br /&gt;
Da die Yahoo Weather API zum 03.01.2019 abgeschaltet wurde, kann Weather keine Wetterdaten von Yahoo mehr beziehen.&lt;br /&gt;
&lt;br /&gt;
== Unwetterzentrale ==&lt;br /&gt;
Informationen der Seite(n) Unwetterzentrale.de können mit Hilfe des Moduls [[UWZ]] eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
== Modul OPENWEATHER ==&lt;br /&gt;
Das Modul [[OPENWEATHER]] extrahiert Wetterdaten über die &amp;quot;openweather&amp;quot;-Schnittstelle (API) von www.wetter.com. &lt;br /&gt;
*Vorschau-Zeitraum: 3 Tage&lt;br /&gt;
*Vorschau-Interval: 6:00, 11:00, 17:00, 23:00&lt;br /&gt;
*Aktuelle Daten: keine&lt;br /&gt;
&lt;br /&gt;
== Modul PROPLANTA ==&lt;br /&gt;
Das Modul [[PROPLANTA]] extrahiert Wetterdaten von der Website www.proplanta.de. (keine API)&lt;br /&gt;
*Vorschau-Zeitraum: 12 Tage&lt;br /&gt;
*Vorschau-Interval: 3 h&lt;br /&gt;
*Aktuelle Daten: jede volle Stunde&lt;br /&gt;
&lt;br /&gt;
== Modul WWO ==&lt;br /&gt;
Das {{Link2CmdRef|Anker=WWO|Label=Modul WWO}} extrahiert Wetterdaten über die (alte) API von worldweatheronline.com&lt;br /&gt;
*Vorschau-Zeitraum: ?? Tage&amp;lt;BR&amp;gt;&lt;br /&gt;
*Vorschau-Interval: ??&amp;lt;br&amp;gt;&lt;br /&gt;
*Aktuelle Daten: ??&lt;br /&gt;
&lt;br /&gt;
== Deutscher Wetterdienst (DWD) ==&lt;br /&gt;
&lt;br /&gt;
=== Modul DWD_OpenData ===&lt;br /&gt;
Das Modul [[DWD_OpenData]] stellt einen Teil der frei verfügbaren Daten des Deutschen Wetterdienstes (DWD) bereit, die über den [https://www.dwd.de/DE/leistungen/opendata/opendata.html Open Data Server] abgerufen werden können (diverse Datenformate, keine API). &lt;br /&gt;
*Vorschau-Zeitraum: 10 Tage&lt;br /&gt;
*Vorschau-Interval: datenabhängig, viele mit 3 h&lt;br /&gt;
*Vorschau-Orte: national und international (weniger Daten, z.B. keine Radiation)&lt;br /&gt;
*Aktuelle Daten: nicht implementiert&lt;br /&gt;
*Wetterwarnungen: national für Deutschland für Landkreise und Gemeinden in deutsch oder englisch&lt;br /&gt;
&lt;br /&gt;
Es gibt zum Modul auch einen Weblink für [[FHEMWEB]], der zwei Icons pro Tag in horizontaler Form darstellt. Die Anzahl der insgesamt dargestellten Icons ist konfigurierbar. Liegen Wetterwarnungen vor, wird zusätzlich ein Warnsymbol angezeigt, über das man per Klick weitere Details zur Wetterwarnung abrufen kann.&lt;br /&gt;
&lt;br /&gt;
[[DWD_OpenData|Weitere Details und Installationsbeschreibung]]&lt;br /&gt;
&lt;br /&gt;
=== Wetterwarnungen als Karte ===&lt;br /&gt;
Der Deutsche Wetterdienst [http://www.dwd.de DWD] stellt neben diversen Wetterdaten auch Wetterwarnungen in Bildform bereit. Diese können sehr einfach via [http://fhem.de/commandref_DE.html#weblink weblink iframe] in FHEM eingebunden werden - das ist zwar nicht die schönste Form, aber die schnellste.&lt;br /&gt;
&lt;br /&gt;
Dazu sucht man sich zuerst auf der [http://www.dwd.de/bvbw/appmanager/bvbw/dwdwwwDesktop?_nfpb=true&amp;amp;_pageLabel=_dwdwww_wetter_warnungen_warnungen&amp;amp;T92053gsbDocumentPath=BEA__Navigation%2FWetter__Warnungen%2FWarnungen.html%3F__nnn%3Dtrue&amp;amp;lastPageLabel=dwdwww_wetter_warnungen Warnungsseite des DWD] die eigene Region heraus (in der Karte durchklicken). Die gewünschte URL für Berlin Stadt ist z.B. &amp;lt;code&amp;gt;http://www.dwd.de/dyn/app/ws/html/reports/BXX_warning_de.html#WS_ANCHOR_0&amp;lt;/code&amp;gt;. Das Anhängsel &amp;lt;code&amp;gt;#WS_ANCHOR_0&amp;lt;/code&amp;gt; kann man in der Konfiguration auch weglassen.&lt;br /&gt;
&lt;br /&gt;
Die Einbindung in FHEM sieht dann wie folgt aus:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define dwd_warnmeldung weblink iframe http://www.dwd.de/dyn/app/ws/html/reports/BXX_warning_de.html&lt;br /&gt;
attr dwd_warnmeldung group Wetterwarnungen (DWD)&lt;br /&gt;
attr dwd_warnmeldung htmlattr width=&amp;quot;100%&amp;quot; height=&amp;quot;300&amp;quot; frameborder=&amp;quot;0&amp;quot; marginheight=&amp;quot;0&amp;quot; marginwidth=&amp;quot;0&amp;quot;&lt;br /&gt;
attr dwd_warnmeldung room Wettervorhersage,Startseite&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Modul CDCOpenData ===&lt;br /&gt;
Das Modul [[CDCOpenData]] stellt einen Teil der frei verfügbaren Daten des DWD-Climate Data Center (CDC) bereit, die über den [https://opendata.dwd.de/climate_environment/CDC/  CDC Open Data Server] abgerufen werden können (diverse Datenformate, keine API). Es handelt sich dabei um Klimadaten, die zum Teil mehrere Jahre zurückreichen. &lt;br /&gt;
&lt;br /&gt;
Besonders interessant sind die pro Tag gefallenen Regenmengen. Sie beruhen auf Regenradar-Messungen, deren Werte an die gemessenen Mengen der Wetterstationen angeeicht wurden. Die räumliche Auflösung beträgt dabei 1 km. Das Modul 98_CDCOpenData.pm ermittelt die innerhalb 24 Stunden gefallenen Regenmengen der letzten Tage sowie die seit Mitternacht gefallene Regenmenge. &lt;br /&gt;
&lt;br /&gt;
Das kann z.B. für eine Bewässerungssteuerung genutzt werden, genauso wie die für die nächsten 2 Stunden vorhergesagten Regenintensitäten, die ebenfalls mit dem Modul abrufbar sind. &lt;br /&gt;
&lt;br /&gt;
[[CDCOpenData|Weitere Details und Installationsbeschreibung]]&lt;br /&gt;
&lt;br /&gt;
== Vorhersage-Diagramm von YR ==&lt;br /&gt;
[http://www.yr.no YR] ist die Seite des norwegischen Wetterdiensts. Er ist zwar nur in Teilen auf Englisch (und gar nicht auf Deutsch) verfügbar, bietet aber sehr gute Wettervorhersagen - auch mit eigenen Vorhersagen für kleine Dörfer. Neben etlichen Detailinfos bietet YR eine sehr schicke 48h-Vorhersage-Grafik. Diese kann sehr einfach via [http://fhem.de/commandref_DE.html#weblink weblink iframe] in FHEM eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Dazu geht man auf die [http://www.yr.no Startseite von YR] und gibt in das Suchfeld den Namen seines Dörfchens ein - die Ergebnissliste kann ziemlich lang werden, sodass man evtl. sehr genau schauen muss, welches das eigene Kaff ist - bei mir war es an 29. Stelle... ;-) Auf der gewünschten Seite gibt es einen Unterpunkt &amp;quot;Forecast as SVG&amp;quot;, wo sich die besagte Grafik findet. Hier kopiert man sich einfach die URL der Grafik.&lt;br /&gt;
&lt;br /&gt;
Die Einbindung in FHEM sieht dann bspw. wie folgt aus:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Wetter_yr_Vorhersage weblink iframe https://www.yr.no/en/content/2-2950159/meteogram.svg&lt;br /&gt;
attr Wetter_yr_Vorhersage group Wettervorhersage (yr)&lt;br /&gt;
attr Wetter_yr_Vorhersage htmlattr width=&amp;quot;782&amp;quot; height=&amp;quot;391&amp;quot; frameborder=&amp;quot;0&amp;quot; marginheight=&amp;quot;0&amp;quot; marginwidth=&amp;quot;0&amp;quot;&lt;br /&gt;
attr Wetter_yr_Vorhersage room Wettervorhersage,Start&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wetter von Weather Underground ==&lt;br /&gt;
{{Randnotiz|RNTyp=r|RNText=Das (&amp;quot;offene&amp;quot;) API von Weather Underground wird derzeit (März 2019) &amp;quot;retired&amp;quot; = abgestellt, ist also künftig nicht mehr nutzbar! Den benötigten API-Key bekommen &amp;quot;nur noch&amp;quot; Betreiber einer Wetterstation, die diese bei wundergound registrieren und ihre Daten hochladen.}}&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; Die hier beschriebene Vorgehensweise ist etwas anspruchsvoller und somit eher für erfahrene FHEM-User geeignet.&lt;br /&gt;
&lt;br /&gt;
[http://www.wunderground.com/ Weather Underground] bietet eine sehr umfassende Sammlung aller möglichen offiziellen und privaten (aber teils sehr professionellen) Wetterstationen weltweit. Es besteht eine recht hohe Wahrscheinlichkeit, dass es in der eigenen Umgebung schon eine Wetterstation gibt. Anstatt nun selbst eine komplette Wetterstation aufzubauen, kann man alternativ auf die Daten der Nachbarstation zugreifen.&lt;br /&gt;
&lt;br /&gt;
{{Randnotiz|RNTyp=y|RNText=Neben dem hier vorgestellten Abruf der Weather Underground-Daten mit [[HTTPMOD]] kann auf Weather Underground-Daten auch mit dem darauf spezialisierten Modul [http://fhem.de/commandref.html#Wunderground Wunderground] zugegriffen werden.}}&lt;br /&gt;
&lt;br /&gt;
Dazu sind grundsätzlich die folgenden Schritte notwendig:&lt;br /&gt;
* Finden der geeigneten Station&lt;br /&gt;
* Auswerten der XML-Daten&lt;br /&gt;
* Erstellen passender gplot-Dateien&lt;br /&gt;
&lt;br /&gt;
=== Finden der geeigneten Station ===&lt;br /&gt;
Als erstes geht man auf die [http://www.wunderground.com Startseite von Weather Underground] und gibt in das Suchfeld den Namen seines Dörfchens oder der nächsten Stadt ein - für Berlin landet man bspw. auf Seite für &amp;quot;Berlin Alexanderplatz&amp;quot;. Es gibt aber manchmal sehr viele Wetterstationen in der direkten Umgebung. Diese findet man indem am auf &amp;quot;Change Station&amp;quot; klickt und sich eine passende Station aussucht. Nähere Infos (zB. zur verwendeten Hard- und Software) zu der gewählten Wetterstation erhält man, wenn man auf den Namen der Station klickt und dann auf &amp;quot;About this PWS&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
Weather Underground stellt die Daten der Wetterstation auch über eine API als XML bereit, die wir auswerten können. Die entsprechende URL findet sich ebenfalls unter &amp;quot;About this PWS&amp;quot; unter &amp;quot;Download current conditions XML&amp;quot;. Für Berlin Prenzlauer Berg wäre dies z.B. &lt;br /&gt;
&amp;lt;code&amp;gt;http://api.wunderground.com/weatherstation/WXDailyHistory.asp?ID=IBERLIN15&amp;amp;format=XML&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entscheidend für die jeweilige Station ist der Teil nach &amp;quot;?=ID&amp;quot;, also in diesem Fall &amp;quot;IBERLIN15&amp;quot;. Dieser Teil des Links lässt sich leicht durch die ID einer anderen Station ersetzen und die Suche nach dem entsprechenden Link auf der Seite der Station bleibt erspart.&lt;br /&gt;
&lt;br /&gt;
=== Auswerten der XML-Daten ===&lt;br /&gt;
Die Auswertung der XML-Daten erfolgt via [[HTTPMOD]]. Da dies nicht ganz trivial ist, sind im Folgenden zuerst die wesentlichen Konfigurationsschritte exemplarisch beschrieben. Weiter unten findet sich dann die fertige vollständige Konfiguration.&lt;br /&gt;
&lt;br /&gt;
Es erstes wird das Device HTTPMOD definiert (die 600 am Schluss ist der Aktualisierungszyklus in Sekunden, also alle 10 min):&lt;br /&gt;
:&amp;lt;code&amp;gt;define wetter_prenzelberg HTTPMOD http://api.wunderground.com/weatherstation/WXCurrentObXML.asp?ID=IBERLIN15 600 &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann werden Attribute für alle Readings definiert:&lt;br /&gt;
:&amp;lt;code&amp;gt;attr wetter_prenzelberg userattr readingsName_cloudiness readingsName_date ... &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann wird jedem Attribut ein Name zugewiesen (unter diesem Namen wird der Wert ins Log geschrieben):&lt;br /&gt;
:&amp;lt;code&amp;gt;attr wetter_prenzelberg readingsName_cloudiness cloudiness &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schluss wird das Reading selbst definiert:&lt;br /&gt;
:&amp;lt;code&amp;gt;attr wetter_prenzelberg readingsRegex_cloudiness cloudiness id=&amp;quot;NN&amp;quot; percent=&amp;quot;([\d\.]+) &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die gesamte Konfiguration für alle von der API angebotenen Wetterwerte sieht dann wie folgt aus:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define wetter_prenzelberg HTTPMOD http://api.wunderground.com/weatherstation/WXCurrentObXML.asp?ID=IBERLIN15 600&lt;br /&gt;
attr wetter_prenzelberg userattr readingsName_cloudiness readingsName_date readingsName_dewpointTemperature readingsName_fog readingsName_heatindex readingsName_humidity readingsName_pressure readingsName_solarRadiation readingsName_solarUV readingsName_temperature readingsName_time readingsName_windChill readingsName_windDegrees readingsName_windDirection readingsName_windGust readingsName_windSpeed readingsRegex_cloudiness readingsRegex_date readingsRegex_dewpointTemperature readingsRegex_fog readingsRegex_heatindex readingsRegex_humidity readingsRegex_pressure readingsRegex_solarRadiation readingsRegex_solarUV readingsRegex_temperature readingsRegex_time readingsRegex_windChill readingsRegex_windDegrees readingsRegex_windDirection readingsRegex_windGust readingsRegex_windSpeed&lt;br /&gt;
attr wetter_prenzelberg readingsName_cloudiness cloudiness&lt;br /&gt;
attr wetter_prenzelberg readingsName_date date&lt;br /&gt;
attr wetter_prenzelberg readingsName_dewpointTemperature dewpointTemperature&lt;br /&gt;
attr wetter_prenzelberg readingsName_fog fog&lt;br /&gt;
attr wetter_prenzelberg readingsName_heatindex heatindex&lt;br /&gt;
attr wetter_prenzelberg readingsName_humidity humidity&lt;br /&gt;
attr wetter_prenzelberg readingsName_pressure pressure&lt;br /&gt;
attr wetter_prenzelberg readingsName_solarRadiation solarRadiation&lt;br /&gt;
attr wetter_prenzelberg readingsName_solarUV solarUV&lt;br /&gt;
attr wetter_prenzelberg readingsName_temperature temperature&lt;br /&gt;
attr wetter_prenzelberg readingsName_time time&lt;br /&gt;
attr wetter_prenzelberg readingsName_windChill windChill&lt;br /&gt;
attr wetter_prenzelberg readingsName_windDegrees windDegrees&lt;br /&gt;
attr wetter_prenzelberg readingsName_windDirection windDirection&lt;br /&gt;
attr wetter_prenzelberg readingsName_windGust windGust&lt;br /&gt;
attr wetter_prenzelberg readingsName_windSpeed windSpeed&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_cloudiness cloudiness id=&amp;quot;NN&amp;quot; percent=&amp;quot;([\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_date date date&amp;quot; content=&amp;quot;([\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_dewpointTemperature &amp;lt;dewpoint_c&amp;gt;(\+|-?[\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_fog fog id=&amp;quot;FOG&amp;quot; percent=&amp;quot;([\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_heatindex &amp;lt;heat_index_c&amp;gt;(\+|-?[\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_humidity &amp;lt;relative_humidity&amp;gt;([\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_pressure &amp;lt;pressure_mb&amp;gt;([\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_solarRadiation &amp;lt;solar_radiation&amp;gt;([\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_solarUV &amp;lt;UV&amp;gt;([\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_temperature &amp;lt;temp_c&amp;gt;(\+|-?[\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_time time Zeit([\d\:]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_windChill &amp;lt;windchill_c&amp;gt;(\+|-?[\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_windDegrees &amp;lt;wind_degrees&amp;gt;([\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_windDirection &amp;lt;wind_dir&amp;gt;([\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_windGust &amp;lt;wind_gust_mph&amp;gt;([\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_windSpeed &amp;lt;wind_mph&amp;gt;([\d\.]+)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Hinweis: Nicht immer werden alle Werte geliefert (wenn keine Sonne, dann kein UV-Wert...). Im System-Log kann das als Fehler auftauchen, ist aber kein Problem.&lt;br /&gt;
&lt;br /&gt;
=== Erstellen passender gplot-Dateien ===&lt;br /&gt;
Um die ausgelesenen Werte als Graphen darstellen zu können müssen eigene gplot-Dateien erstellt werden. Eine grundsätzliche Anleitung dazu findet sich unter [[Creating Plots]]. Deshalb sollen hier nur Beispiele für die eigentlichen gplot-Dateien vorgestellt werden. Beispiele sollten sowohl für [[FileLog]] wie [[DbLog]] funktionieren.&lt;br /&gt;
&lt;br /&gt;
Temperatur und Luftfeuchte:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set terminal png transparent size &amp;lt;SIZE&amp;gt; crop&lt;br /&gt;
set output &#039;&amp;lt;OUT&amp;gt;.png&#039;&lt;br /&gt;
set xdata time&lt;br /&gt;
set timefmt &amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;&lt;br /&gt;
set xlabel &amp;quot; &amp;quot;&lt;br /&gt;
#set title &#039;&amp;lt;L1&amp;gt;&#039;&lt;br /&gt;
set title &#039;&#039;&lt;br /&gt;
set ytics &lt;br /&gt;
set y2tics &lt;br /&gt;
set grid ytics&lt;br /&gt;
set ylabel &amp;quot;Temperature in C&amp;quot;&lt;br /&gt;
set y2label &amp;quot;Humidity (%)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#FileLog 4:temperature\x3a::&lt;br /&gt;
#FileLog 4:windChill\x3a::&lt;br /&gt;
#FileLog 4:humidity\x3a::&lt;br /&gt;
&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:temperature::&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:windChill::&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:humidity::&lt;br /&gt;
&lt;br /&gt;
plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Temperatur (C)&#039; ls l0 lw 1.5 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Gefühlte Temperatur (C)&#039; ls l1 lw 1.5 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Luftfeuchtigkeit (%)&#039; ls l2fill lw 0.2 with lines&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wind und Windrichtung:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set terminal png transparent size &amp;lt;SIZE&amp;gt; crop&lt;br /&gt;
&lt;br /&gt;
set output &#039;&amp;lt;OUT&amp;gt;.png&#039;&lt;br /&gt;
set xdata time&lt;br /&gt;
set timefmt &amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;&lt;br /&gt;
set xlabel &amp;quot; &amp;quot;&lt;br /&gt;
&lt;br /&gt;
set ytics&lt;br /&gt;
set y2tics (&amp;quot;Nord&amp;quot; 0, &amp;quot;Ost&amp;quot; 80, &amp;quot;S&amp;amp;#252;d&amp;quot; 180, &amp;quot;West&amp;quot; 270, &amp;quot;Nord&amp;quot; 360)&lt;br /&gt;
&lt;br /&gt;
#set title &#039;&amp;lt;L1&amp;gt;&#039;&lt;br /&gt;
set title &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
set grid xtics ytics&lt;br /&gt;
&lt;br /&gt;
set yrange [0:20]&lt;br /&gt;
set y2range [0:360]&lt;br /&gt;
&lt;br /&gt;
set ylabel &amp;quot;Wind (mph)&amp;quot;&lt;br /&gt;
set y2label &amp;quot;Richtung&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#FileLog 4:windSpeed:0:&lt;br /&gt;
#FileLog 4:windGust:0:&lt;br /&gt;
#FileLog 4:windDegrees:0:&lt;br /&gt;
&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:windSpeed::&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:windGust::&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:windDegrees::&lt;br /&gt;
&lt;br /&gt;
plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Wind (mph)&#039; ls l0 lw 1.5 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Böen (mph)&#039; ls l1 lw 1.5 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Windrichtung (°)&#039; ls l2fill lw 0.2 with points&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sonnenintensität und UV-Werte:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set terminal png transparent size &amp;lt;SIZE&amp;gt; crop&lt;br /&gt;
&lt;br /&gt;
set output &#039;&amp;lt;OUT&amp;gt;.png&#039;&lt;br /&gt;
set xdata time&lt;br /&gt;
set timefmt &amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;&lt;br /&gt;
set xlabel &amp;quot; &amp;quot;&lt;br /&gt;
&lt;br /&gt;
set ytics&lt;br /&gt;
set y2tics&lt;br /&gt;
&lt;br /&gt;
#set title &#039;&amp;lt;L1&amp;gt;&#039;&lt;br /&gt;
set title &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
set grid xtics ytics&lt;br /&gt;
&lt;br /&gt;
set ylabel &amp;quot;Sonnenstrahlung (W/qm)&amp;quot;&lt;br /&gt;
set y2label &amp;quot;UV&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#FileLog 4:solarRadiation:0:&lt;br /&gt;
#FileLog 4:solarUV:0:&lt;br /&gt;
&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:solarRadiation::&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:solarUV::&lt;br /&gt;
&lt;br /&gt;
plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Strahlung&#039; ls l0 lw 1.5 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;UV&#039; ls l1 lw 1.5 with lines&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere TBD&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Vergangene Daten abfragen ===&lt;br /&gt;
Ein konkretes vergangenes Datum der Wetterstation kann man übrigens auch abrufen - für den 28.11.2014 wäre dies bspw. &lt;br /&gt;
&amp;lt;code&amp;gt;http://www.wunderground.com/weatherstation/WXDailyHistory.asp?ID=IBERLIN15&amp;amp;day=28&amp;amp;month=11&amp;amp;year=2014&amp;amp;graphspan=day&amp;amp;format=1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Eigene Wetterdaten hochladen ===&lt;br /&gt;
{{Randnotiz|RNTyp=g|RNText=Unterstützung für das Modul gibt es im dazugehörigen {{Link2Forum|Topic=65587|LinkText=Forumsthread }}.}}&lt;br /&gt;
Wer eigene Wetterdaten hochladen will, kann hierzu das {{Link2CmdRef|Anker=WUup|Label=Modul WUup}} verwenden. Dazu benötigt man die Zugangsdaten (stationID und Paßwort) für eine eigene Wetterstation. Mit dem Modul können die meisten vom [http://web.archive.org/web/20180815041359/http://wiki.wunderground.com/index.php/PWS_-_Upload_Protocol PWS Upload Protocol] unterstützten Daten hochgeladen werden:&lt;br /&gt;
* Winddir - [0-360 momentane Windrichtung]&lt;br /&gt;
* Windspeedmph - [mph momentane Windgeschwindigkeit]&lt;br /&gt;
* Windgustmph - [mph aktuellen Böe, mit Software-spezifischem Zeitraum]&lt;br /&gt;
* Windgustdir - [0-360 mit Software-spezifischer Zeit]&lt;br /&gt;
* Windspdmph_avg2m - [mph durchschnittliche Windgeschwindigkeit innerhalb 2 Minuten]&lt;br /&gt;
* Winddir_avg2m - [0-360 durchschnittliche Windrichtung innerhalb 2 Minuten]&lt;br /&gt;
* Windgustmph_10m - [mph Böen der vergangenen 10 Minuten]&lt;br /&gt;
* Windgustdir_10m - [0-360 Richtung der Böen der letzten 10 Minuten]&lt;br /&gt;
* Feuchtigkeit - [% Außenfeuchtigkeit 0-100%]&lt;br /&gt;
* Dewptf- [F Taupunkt im Freien]&lt;br /&gt;
* Tempf - [F Außentemperatur]&lt;br /&gt;
* Rainin - [in Regen in der vergangenen Stunde]&lt;br /&gt;
* Dailyrainin - [in Regenmenge bisher heute]&lt;br /&gt;
* Baromin - [inHg barometrischer Druck]&lt;br /&gt;
* Soiltempf - [F Bodentemperatur]&lt;br /&gt;
* Bodenfeuchtigkeit - [%]&lt;br /&gt;
* Solarradiation - [W/m²]&lt;br /&gt;
* UV - [Index]&lt;br /&gt;
* AqPM2.5 - Feinstaub PM2,5 [µg/m³]&lt;br /&gt;
* AqPM10 - Feinstaub PM10 [µg/m³]&lt;br /&gt;
&lt;br /&gt;
Die Werte werden im Modul für Wunderground ins anglo-amerikanische System umgerechnet. Temperaturen werden also automatisch von °Celsius in Fahrenheit, Windgeschwindigkeiten von km/h in mph, mm in Inch und der barometrische Druck von hPa in inHg umgerechnet.&lt;br /&gt;
&lt;br /&gt;
== Wetternetzwerk auf wetter.com ==&lt;br /&gt;
Ein vollständiges Beispiel zur Nutzung der Informationen über die Regenmenge (Station 1445, Weinheim) von wetter.com ist in {{Link2Forum|Topic=39600|Message=332837|LinkText=diesem Forenbeitrag}} beschrieben.&lt;br /&gt;
&lt;br /&gt;
== Wetter von netatmo ==&lt;br /&gt;
Das FHEM-Modul [[netatmo]] ermöglicht den Datenimport von privaten und öffentlichen netatmo Wetterstationen sowie den Abruf der in der Netatmo App verfügbaren Wettervorhersage. Eine Übersicht über die verfügbaren öffentlichen Stationen findet sich auf der Seite [http://www.netatmo.com/de-DE/weathermap Weathermap]. Die meisten öffentlichen Stationen liefern Temperatur, Luftfeuchte und Luftdruck, einige auch Wind, Regen und Daten zur Luftqualität (CO&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* {{Link2Forum|Topic=33610|LinkText=Forenthema}} mit Sammlung und Beipielen von Internet-Links für die Anzeige von teilweise animierten Wetterbildern&lt;br /&gt;
* [http://bloglich.de/art/rpi/wetterdaten-auf-dem-raspberry-pi-in-fhem/ Beispiel in Blog(de)]&lt;br /&gt;
* [[HTTPMOD]]       &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]   &lt;br /&gt;
[[Kategorie:HOWTOS]]   &lt;br /&gt;
[[Kategorie:Wetterstationen]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Wetter_und_Wettervorhersagen&amp;diff=38710</id>
		<title>Wetter und Wettervorhersagen</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Wetter_und_Wettervorhersagen&amp;diff=38710"/>
		<updated>2023-11-10T20:19:57Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* Modul CDCOpenData */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Wetterdaten und Wettervorhersagen können auf verschiedene Weise in FHEM eingebunden werden.&lt;br /&gt;
== Modul Weather ==&lt;br /&gt;
Das Modul [[Weather]] extrahiert Wetterdaten via API von den Wetterdatenanbietern&lt;br /&gt;
* [https://openweathermap.org/ Openweathermap]&lt;br /&gt;
** Vorschauzeitraum: 5 Tage (zeitliche Auflösung: 3 Stunden)&lt;br /&gt;
** Aktuelle Daten (minütlich)&lt;br /&gt;
** 1000 Abrufe pro Tag mit dem Gratis-API-Key (Stand: Juni 2021)&lt;br /&gt;
* [https://www.wunderground.com/ wunderground]&lt;br /&gt;
** API-KEY nur für Gerätebesitzer&lt;br /&gt;
* [https://darksky.net/ DarkSky]&lt;br /&gt;
** Support for the Dark Sky API ended on March 31, 2023, and has been replaced by Apple’s WeatherKit API. &lt;br /&gt;
** Es können keine neuen API-Key erstellt werden (Stand: Juni 2021)&lt;br /&gt;
&lt;br /&gt;
Da die Yahoo Weather API zum 03.01.2019 abgeschaltet wurde, kann Weather keine Wetterdaten von Yahoo mehr beziehen.&lt;br /&gt;
&lt;br /&gt;
== Unwetterzentrale ==&lt;br /&gt;
Informationen der Seite(n) Unwetterzentrale.de können mit Hilfe des Moduls [[UWZ]] eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
== Modul OPENWEATHER ==&lt;br /&gt;
Das Modul [[OPENWEATHER]] extrahiert Wetterdaten über die &amp;quot;openweather&amp;quot;-Schnittstelle (API) von www.wetter.com. &lt;br /&gt;
*Vorschau-Zeitraum: 3 Tage&lt;br /&gt;
*Vorschau-Interval: 6:00, 11:00, 17:00, 23:00&lt;br /&gt;
*Aktuelle Daten: keine&lt;br /&gt;
&lt;br /&gt;
== Modul PROPLANTA ==&lt;br /&gt;
Das Modul [[PROPLANTA]] extrahiert Wetterdaten von der Website www.proplanta.de. (keine API)&lt;br /&gt;
*Vorschau-Zeitraum: 12 Tage&lt;br /&gt;
*Vorschau-Interval: 3 h&lt;br /&gt;
*Aktuelle Daten: jede volle Stunde&lt;br /&gt;
&lt;br /&gt;
== Modul WWO ==&lt;br /&gt;
Das {{Link2CmdRef|Anker=WWO|Label=Modul WWO}} extrahiert Wetterdaten über die (alte) API von worldweatheronline.com&lt;br /&gt;
*Vorschau-Zeitraum: ?? Tage&amp;lt;BR&amp;gt;&lt;br /&gt;
*Vorschau-Interval: ??&amp;lt;br&amp;gt;&lt;br /&gt;
*Aktuelle Daten: ??&lt;br /&gt;
&lt;br /&gt;
== Deutscher Wetterdienst (DWD) ==&lt;br /&gt;
&lt;br /&gt;
=== Modul DWD_OpenData ===&lt;br /&gt;
Das Modul [[DWD_OpenData]] stellt einen Teil der frei verfügbaren Daten des Deutschen Wetterdienstes (DWD) bereit, die über den [https://www.dwd.de/DE/leistungen/opendata/opendata.html Open Data Server] abgerufen werden können (diverse Datenformate, keine API). &lt;br /&gt;
*Vorschau-Zeitraum: 10 Tage&lt;br /&gt;
*Vorschau-Interval: datenabhängig, viele mit 3 h&lt;br /&gt;
*Vorschau-Orte: national und international (weniger Daten, z.B. keine Radiation)&lt;br /&gt;
*Aktuelle Daten: nicht implementiert&lt;br /&gt;
*Wetterwarnungen: national für Deutschland für Landkreise und Gemeinden in deutsch oder englisch&lt;br /&gt;
&lt;br /&gt;
Es gibt zum Modul auch einen Weblink für [[FHEMWEB]], der zwei Icons pro Tag in horizontaler Form darstellt. Die Anzahl der insgesamt dargestellten Icons ist konfigurierbar. Liegen Wetterwarnungen vor, wird zusätzlich ein Warnsymbol angezeigt, über das man per Klick weitere Details zur Wetterwarnung abrufen kann.&lt;br /&gt;
&lt;br /&gt;
[[DWD_OpenData|Weitere Details und Installationsbeschreibung]]&lt;br /&gt;
&lt;br /&gt;
=== Wetterwarnungen als Karte ===&lt;br /&gt;
Der Deutsche Wetterdienst [http://www.dwd.de DWD] stellt neben diversen Wetterdaten auch Wetterwarnungen in Bildform bereit. Diese können sehr einfach via [http://fhem.de/commandref_DE.html#weblink weblink iframe] in FHEM eingebunden werden - das ist zwar nicht die schönste Form, aber die schnellste.&lt;br /&gt;
&lt;br /&gt;
Dazu sucht man sich zuerst auf der [http://www.dwd.de/bvbw/appmanager/bvbw/dwdwwwDesktop?_nfpb=true&amp;amp;_pageLabel=_dwdwww_wetter_warnungen_warnungen&amp;amp;T92053gsbDocumentPath=BEA__Navigation%2FWetter__Warnungen%2FWarnungen.html%3F__nnn%3Dtrue&amp;amp;lastPageLabel=dwdwww_wetter_warnungen Warnungsseite des DWD] die eigene Region heraus (in der Karte durchklicken). Die gewünschte URL für Berlin Stadt ist z.B. &amp;lt;code&amp;gt;http://www.dwd.de/dyn/app/ws/html/reports/BXX_warning_de.html#WS_ANCHOR_0&amp;lt;/code&amp;gt;. Das Anhängsel &amp;lt;code&amp;gt;#WS_ANCHOR_0&amp;lt;/code&amp;gt; kann man in der Konfiguration auch weglassen.&lt;br /&gt;
&lt;br /&gt;
Die Einbindung in FHEM sieht dann wie folgt aus:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define dwd_warnmeldung weblink iframe http://www.dwd.de/dyn/app/ws/html/reports/BXX_warning_de.html&lt;br /&gt;
attr dwd_warnmeldung group Wetterwarnungen (DWD)&lt;br /&gt;
attr dwd_warnmeldung htmlattr width=&amp;quot;100%&amp;quot; height=&amp;quot;300&amp;quot; frameborder=&amp;quot;0&amp;quot; marginheight=&amp;quot;0&amp;quot; marginwidth=&amp;quot;0&amp;quot;&lt;br /&gt;
attr dwd_warnmeldung room Wettervorhersage,Startseite&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Modul CDCOpenData ===&lt;br /&gt;
Das Modul [[CDCOpenData]] stellt einen Teil der frei verfügbaren Daten des DWD-Climate Data Center (CDC) bereit, die über den [https://opendata.dwd.de/climate_environment/CDC/  CDC Open Data Server] abgerufen werden können (diverse Datenformate, keine API). Es handelt sich dabei um Klimadaten, die zum Teil mehrere Jahre zurückreichen. &lt;br /&gt;
&lt;br /&gt;
Besonders interessant sind die pro Tag gefallenen Regenmengen. Sie beruhen auf Regenradar-Messungen, deren Werte an die gemessenen Mengen der Wetterstationen angeeicht wurden. Die räumliche Auflösung beträgt dabei 1 km. Das Modul 98_CDCOpenData.pm ermittelt die innerhalb 24 Stunden gefallenen Regenmengen der letzten Tage sowie die seit Mitternacht gefallene Regenmenge. &lt;br /&gt;
&lt;br /&gt;
Das kann z.B. für eine Bewässerungssteuerung genutzt werden, genauso wie die für die nächsten 2 Stunden vorhergesagten Regenintensitäten, die ebenfalls mit dem Modul abrufbat sind. &lt;br /&gt;
&lt;br /&gt;
[[CDCOpenData|Weitere Details und Installationsbeschreibung]]&lt;br /&gt;
&lt;br /&gt;
== Vorhersage-Diagramm von YR ==&lt;br /&gt;
[http://www.yr.no YR] ist die Seite des norwegischen Wetterdiensts. Er ist zwar nur in Teilen auf Englisch (und gar nicht auf Deutsch) verfügbar, bietet aber sehr gute Wettervorhersagen - auch mit eigenen Vorhersagen für kleine Dörfer. Neben etlichen Detailinfos bietet YR eine sehr schicke 48h-Vorhersage-Grafik. Diese kann sehr einfach via [http://fhem.de/commandref_DE.html#weblink weblink iframe] in FHEM eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
Dazu geht man auf die [http://www.yr.no Startseite von YR] und gibt in das Suchfeld den Namen seines Dörfchens ein - die Ergebnissliste kann ziemlich lang werden, sodass man evtl. sehr genau schauen muss, welches das eigene Kaff ist - bei mir war es an 29. Stelle... ;-) Auf der gewünschten Seite gibt es einen Unterpunkt &amp;quot;Forecast as SVG&amp;quot;, wo sich die besagte Grafik findet. Hier kopiert man sich einfach die URL der Grafik.&lt;br /&gt;
&lt;br /&gt;
Die Einbindung in FHEM sieht dann bspw. wie folgt aus:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Wetter_yr_Vorhersage weblink iframe https://www.yr.no/en/content/2-2950159/meteogram.svg&lt;br /&gt;
attr Wetter_yr_Vorhersage group Wettervorhersage (yr)&lt;br /&gt;
attr Wetter_yr_Vorhersage htmlattr width=&amp;quot;782&amp;quot; height=&amp;quot;391&amp;quot; frameborder=&amp;quot;0&amp;quot; marginheight=&amp;quot;0&amp;quot; marginwidth=&amp;quot;0&amp;quot;&lt;br /&gt;
attr Wetter_yr_Vorhersage room Wettervorhersage,Start&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wetter von Weather Underground ==&lt;br /&gt;
{{Randnotiz|RNTyp=r|RNText=Das (&amp;quot;offene&amp;quot;) API von Weather Underground wird derzeit (März 2019) &amp;quot;retired&amp;quot; = abgestellt, ist also künftig nicht mehr nutzbar! Den benötigten API-Key bekommen &amp;quot;nur noch&amp;quot; Betreiber einer Wetterstation, die diese bei wundergound registrieren und ihre Daten hochladen.}}&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; Die hier beschriebene Vorgehensweise ist etwas anspruchsvoller und somit eher für erfahrene FHEM-User geeignet.&lt;br /&gt;
&lt;br /&gt;
[http://www.wunderground.com/ Weather Underground] bietet eine sehr umfassende Sammlung aller möglichen offiziellen und privaten (aber teils sehr professionellen) Wetterstationen weltweit. Es besteht eine recht hohe Wahrscheinlichkeit, dass es in der eigenen Umgebung schon eine Wetterstation gibt. Anstatt nun selbst eine komplette Wetterstation aufzubauen, kann man alternativ auf die Daten der Nachbarstation zugreifen.&lt;br /&gt;
&lt;br /&gt;
{{Randnotiz|RNTyp=y|RNText=Neben dem hier vorgestellten Abruf der Weather Underground-Daten mit [[HTTPMOD]] kann auf Weather Underground-Daten auch mit dem darauf spezialisierten Modul [http://fhem.de/commandref.html#Wunderground Wunderground] zugegriffen werden.}}&lt;br /&gt;
&lt;br /&gt;
Dazu sind grundsätzlich die folgenden Schritte notwendig:&lt;br /&gt;
* Finden der geeigneten Station&lt;br /&gt;
* Auswerten der XML-Daten&lt;br /&gt;
* Erstellen passender gplot-Dateien&lt;br /&gt;
&lt;br /&gt;
=== Finden der geeigneten Station ===&lt;br /&gt;
Als erstes geht man auf die [http://www.wunderground.com Startseite von Weather Underground] und gibt in das Suchfeld den Namen seines Dörfchens oder der nächsten Stadt ein - für Berlin landet man bspw. auf Seite für &amp;quot;Berlin Alexanderplatz&amp;quot;. Es gibt aber manchmal sehr viele Wetterstationen in der direkten Umgebung. Diese findet man indem am auf &amp;quot;Change Station&amp;quot; klickt und sich eine passende Station aussucht. Nähere Infos (zB. zur verwendeten Hard- und Software) zu der gewählten Wetterstation erhält man, wenn man auf den Namen der Station klickt und dann auf &amp;quot;About this PWS&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
Weather Underground stellt die Daten der Wetterstation auch über eine API als XML bereit, die wir auswerten können. Die entsprechende URL findet sich ebenfalls unter &amp;quot;About this PWS&amp;quot; unter &amp;quot;Download current conditions XML&amp;quot;. Für Berlin Prenzlauer Berg wäre dies z.B. &lt;br /&gt;
&amp;lt;code&amp;gt;http://api.wunderground.com/weatherstation/WXDailyHistory.asp?ID=IBERLIN15&amp;amp;format=XML&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Entscheidend für die jeweilige Station ist der Teil nach &amp;quot;?=ID&amp;quot;, also in diesem Fall &amp;quot;IBERLIN15&amp;quot;. Dieser Teil des Links lässt sich leicht durch die ID einer anderen Station ersetzen und die Suche nach dem entsprechenden Link auf der Seite der Station bleibt erspart.&lt;br /&gt;
&lt;br /&gt;
=== Auswerten der XML-Daten ===&lt;br /&gt;
Die Auswertung der XML-Daten erfolgt via [[HTTPMOD]]. Da dies nicht ganz trivial ist, sind im Folgenden zuerst die wesentlichen Konfigurationsschritte exemplarisch beschrieben. Weiter unten findet sich dann die fertige vollständige Konfiguration.&lt;br /&gt;
&lt;br /&gt;
Es erstes wird das Device HTTPMOD definiert (die 600 am Schluss ist der Aktualisierungszyklus in Sekunden, also alle 10 min):&lt;br /&gt;
:&amp;lt;code&amp;gt;define wetter_prenzelberg HTTPMOD http://api.wunderground.com/weatherstation/WXCurrentObXML.asp?ID=IBERLIN15 600 &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann werden Attribute für alle Readings definiert:&lt;br /&gt;
:&amp;lt;code&amp;gt;attr wetter_prenzelberg userattr readingsName_cloudiness readingsName_date ... &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann wird jedem Attribut ein Name zugewiesen (unter diesem Namen wird der Wert ins Log geschrieben):&lt;br /&gt;
:&amp;lt;code&amp;gt;attr wetter_prenzelberg readingsName_cloudiness cloudiness &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Schluss wird das Reading selbst definiert:&lt;br /&gt;
:&amp;lt;code&amp;gt;attr wetter_prenzelberg readingsRegex_cloudiness cloudiness id=&amp;quot;NN&amp;quot; percent=&amp;quot;([\d\.]+) &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die gesamte Konfiguration für alle von der API angebotenen Wetterwerte sieht dann wie folgt aus:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define wetter_prenzelberg HTTPMOD http://api.wunderground.com/weatherstation/WXCurrentObXML.asp?ID=IBERLIN15 600&lt;br /&gt;
attr wetter_prenzelberg userattr readingsName_cloudiness readingsName_date readingsName_dewpointTemperature readingsName_fog readingsName_heatindex readingsName_humidity readingsName_pressure readingsName_solarRadiation readingsName_solarUV readingsName_temperature readingsName_time readingsName_windChill readingsName_windDegrees readingsName_windDirection readingsName_windGust readingsName_windSpeed readingsRegex_cloudiness readingsRegex_date readingsRegex_dewpointTemperature readingsRegex_fog readingsRegex_heatindex readingsRegex_humidity readingsRegex_pressure readingsRegex_solarRadiation readingsRegex_solarUV readingsRegex_temperature readingsRegex_time readingsRegex_windChill readingsRegex_windDegrees readingsRegex_windDirection readingsRegex_windGust readingsRegex_windSpeed&lt;br /&gt;
attr wetter_prenzelberg readingsName_cloudiness cloudiness&lt;br /&gt;
attr wetter_prenzelberg readingsName_date date&lt;br /&gt;
attr wetter_prenzelberg readingsName_dewpointTemperature dewpointTemperature&lt;br /&gt;
attr wetter_prenzelberg readingsName_fog fog&lt;br /&gt;
attr wetter_prenzelberg readingsName_heatindex heatindex&lt;br /&gt;
attr wetter_prenzelberg readingsName_humidity humidity&lt;br /&gt;
attr wetter_prenzelberg readingsName_pressure pressure&lt;br /&gt;
attr wetter_prenzelberg readingsName_solarRadiation solarRadiation&lt;br /&gt;
attr wetter_prenzelberg readingsName_solarUV solarUV&lt;br /&gt;
attr wetter_prenzelberg readingsName_temperature temperature&lt;br /&gt;
attr wetter_prenzelberg readingsName_time time&lt;br /&gt;
attr wetter_prenzelberg readingsName_windChill windChill&lt;br /&gt;
attr wetter_prenzelberg readingsName_windDegrees windDegrees&lt;br /&gt;
attr wetter_prenzelberg readingsName_windDirection windDirection&lt;br /&gt;
attr wetter_prenzelberg readingsName_windGust windGust&lt;br /&gt;
attr wetter_prenzelberg readingsName_windSpeed windSpeed&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_cloudiness cloudiness id=&amp;quot;NN&amp;quot; percent=&amp;quot;([\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_date date date&amp;quot; content=&amp;quot;([\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_dewpointTemperature &amp;lt;dewpoint_c&amp;gt;(\+|-?[\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_fog fog id=&amp;quot;FOG&amp;quot; percent=&amp;quot;([\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_heatindex &amp;lt;heat_index_c&amp;gt;(\+|-?[\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_humidity &amp;lt;relative_humidity&amp;gt;([\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_pressure &amp;lt;pressure_mb&amp;gt;([\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_solarRadiation &amp;lt;solar_radiation&amp;gt;([\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_solarUV &amp;lt;UV&amp;gt;([\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_temperature &amp;lt;temp_c&amp;gt;(\+|-?[\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_time time Zeit([\d\:]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_windChill &amp;lt;windchill_c&amp;gt;(\+|-?[\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_windDegrees &amp;lt;wind_degrees&amp;gt;([\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_windDirection &amp;lt;wind_dir&amp;gt;([\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_windGust &amp;lt;wind_gust_mph&amp;gt;([\d\.]+)&lt;br /&gt;
attr wetter_prenzelberg readingsRegex_windSpeed &amp;lt;wind_mph&amp;gt;([\d\.]+)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Hinweis: Nicht immer werden alle Werte geliefert (wenn keine Sonne, dann kein UV-Wert...). Im System-Log kann das als Fehler auftauchen, ist aber kein Problem.&lt;br /&gt;
&lt;br /&gt;
=== Erstellen passender gplot-Dateien ===&lt;br /&gt;
Um die ausgelesenen Werte als Graphen darstellen zu können müssen eigene gplot-Dateien erstellt werden. Eine grundsätzliche Anleitung dazu findet sich unter [[Creating Plots]]. Deshalb sollen hier nur Beispiele für die eigentlichen gplot-Dateien vorgestellt werden. Beispiele sollten sowohl für [[FileLog]] wie [[DbLog]] funktionieren.&lt;br /&gt;
&lt;br /&gt;
Temperatur und Luftfeuchte:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set terminal png transparent size &amp;lt;SIZE&amp;gt; crop&lt;br /&gt;
set output &#039;&amp;lt;OUT&amp;gt;.png&#039;&lt;br /&gt;
set xdata time&lt;br /&gt;
set timefmt &amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;&lt;br /&gt;
set xlabel &amp;quot; &amp;quot;&lt;br /&gt;
#set title &#039;&amp;lt;L1&amp;gt;&#039;&lt;br /&gt;
set title &#039;&#039;&lt;br /&gt;
set ytics &lt;br /&gt;
set y2tics &lt;br /&gt;
set grid ytics&lt;br /&gt;
set ylabel &amp;quot;Temperature in C&amp;quot;&lt;br /&gt;
set y2label &amp;quot;Humidity (%)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#FileLog 4:temperature\x3a::&lt;br /&gt;
#FileLog 4:windChill\x3a::&lt;br /&gt;
#FileLog 4:humidity\x3a::&lt;br /&gt;
&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:temperature::&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:windChill::&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:humidity::&lt;br /&gt;
&lt;br /&gt;
plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Temperatur (C)&#039; ls l0 lw 1.5 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Gefühlte Temperatur (C)&#039; ls l1 lw 1.5 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Luftfeuchtigkeit (%)&#039; ls l2fill lw 0.2 with lines&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wind und Windrichtung:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set terminal png transparent size &amp;lt;SIZE&amp;gt; crop&lt;br /&gt;
&lt;br /&gt;
set output &#039;&amp;lt;OUT&amp;gt;.png&#039;&lt;br /&gt;
set xdata time&lt;br /&gt;
set timefmt &amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;&lt;br /&gt;
set xlabel &amp;quot; &amp;quot;&lt;br /&gt;
&lt;br /&gt;
set ytics&lt;br /&gt;
set y2tics (&amp;quot;Nord&amp;quot; 0, &amp;quot;Ost&amp;quot; 80, &amp;quot;S&amp;amp;#252;d&amp;quot; 180, &amp;quot;West&amp;quot; 270, &amp;quot;Nord&amp;quot; 360)&lt;br /&gt;
&lt;br /&gt;
#set title &#039;&amp;lt;L1&amp;gt;&#039;&lt;br /&gt;
set title &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
set grid xtics ytics&lt;br /&gt;
&lt;br /&gt;
set yrange [0:20]&lt;br /&gt;
set y2range [0:360]&lt;br /&gt;
&lt;br /&gt;
set ylabel &amp;quot;Wind (mph)&amp;quot;&lt;br /&gt;
set y2label &amp;quot;Richtung&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#FileLog 4:windSpeed:0:&lt;br /&gt;
#FileLog 4:windGust:0:&lt;br /&gt;
#FileLog 4:windDegrees:0:&lt;br /&gt;
&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:windSpeed::&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:windGust::&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:windDegrees::&lt;br /&gt;
&lt;br /&gt;
plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Wind (mph)&#039; ls l0 lw 1.5 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Böen (mph)&#039; ls l1 lw 1.5 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Windrichtung (°)&#039; ls l2fill lw 0.2 with points&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sonnenintensität und UV-Werte:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set terminal png transparent size &amp;lt;SIZE&amp;gt; crop&lt;br /&gt;
&lt;br /&gt;
set output &#039;&amp;lt;OUT&amp;gt;.png&#039;&lt;br /&gt;
set xdata time&lt;br /&gt;
set timefmt &amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;&lt;br /&gt;
set xlabel &amp;quot; &amp;quot;&lt;br /&gt;
&lt;br /&gt;
set ytics&lt;br /&gt;
set y2tics&lt;br /&gt;
&lt;br /&gt;
#set title &#039;&amp;lt;L1&amp;gt;&#039;&lt;br /&gt;
set title &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
set grid xtics ytics&lt;br /&gt;
&lt;br /&gt;
set ylabel &amp;quot;Sonnenstrahlung (W/qm)&amp;quot;&lt;br /&gt;
set y2label &amp;quot;UV&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#FileLog 4:solarRadiation:0:&lt;br /&gt;
#FileLog 4:solarUV:0:&lt;br /&gt;
&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:solarRadiation::&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:solarUV::&lt;br /&gt;
&lt;br /&gt;
plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Strahlung&#039; ls l0 lw 1.5 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;UV&#039; ls l1 lw 1.5 with lines&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Weitere TBD&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Vergangene Daten abfragen ===&lt;br /&gt;
Ein konkretes vergangenes Datum der Wetterstation kann man übrigens auch abrufen - für den 28.11.2014 wäre dies bspw. &lt;br /&gt;
&amp;lt;code&amp;gt;http://www.wunderground.com/weatherstation/WXDailyHistory.asp?ID=IBERLIN15&amp;amp;day=28&amp;amp;month=11&amp;amp;year=2014&amp;amp;graphspan=day&amp;amp;format=1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Eigene Wetterdaten hochladen ===&lt;br /&gt;
{{Randnotiz|RNTyp=g|RNText=Unterstützung für das Modul gibt es im dazugehörigen {{Link2Forum|Topic=65587|LinkText=Forumsthread }}.}}&lt;br /&gt;
Wer eigene Wetterdaten hochladen will, kann hierzu das {{Link2CmdRef|Anker=WUup|Label=Modul WUup}} verwenden. Dazu benötigt man die Zugangsdaten (stationID und Paßwort) für eine eigene Wetterstation. Mit dem Modul können die meisten vom [http://web.archive.org/web/20180815041359/http://wiki.wunderground.com/index.php/PWS_-_Upload_Protocol PWS Upload Protocol] unterstützten Daten hochgeladen werden:&lt;br /&gt;
* Winddir - [0-360 momentane Windrichtung]&lt;br /&gt;
* Windspeedmph - [mph momentane Windgeschwindigkeit]&lt;br /&gt;
* Windgustmph - [mph aktuellen Böe, mit Software-spezifischem Zeitraum]&lt;br /&gt;
* Windgustdir - [0-360 mit Software-spezifischer Zeit]&lt;br /&gt;
* Windspdmph_avg2m - [mph durchschnittliche Windgeschwindigkeit innerhalb 2 Minuten]&lt;br /&gt;
* Winddir_avg2m - [0-360 durchschnittliche Windrichtung innerhalb 2 Minuten]&lt;br /&gt;
* Windgustmph_10m - [mph Böen der vergangenen 10 Minuten]&lt;br /&gt;
* Windgustdir_10m - [0-360 Richtung der Böen der letzten 10 Minuten]&lt;br /&gt;
* Feuchtigkeit - [% Außenfeuchtigkeit 0-100%]&lt;br /&gt;
* Dewptf- [F Taupunkt im Freien]&lt;br /&gt;
* Tempf - [F Außentemperatur]&lt;br /&gt;
* Rainin - [in Regen in der vergangenen Stunde]&lt;br /&gt;
* Dailyrainin - [in Regenmenge bisher heute]&lt;br /&gt;
* Baromin - [inHg barometrischer Druck]&lt;br /&gt;
* Soiltempf - [F Bodentemperatur]&lt;br /&gt;
* Bodenfeuchtigkeit - [%]&lt;br /&gt;
* Solarradiation - [W/m²]&lt;br /&gt;
* UV - [Index]&lt;br /&gt;
* AqPM2.5 - Feinstaub PM2,5 [µg/m³]&lt;br /&gt;
* AqPM10 - Feinstaub PM10 [µg/m³]&lt;br /&gt;
&lt;br /&gt;
Die Werte werden im Modul für Wunderground ins anglo-amerikanische System umgerechnet. Temperaturen werden also automatisch von °Celsius in Fahrenheit, Windgeschwindigkeiten von km/h in mph, mm in Inch und der barometrische Druck von hPa in inHg umgerechnet.&lt;br /&gt;
&lt;br /&gt;
== Wetternetzwerk auf wetter.com ==&lt;br /&gt;
Ein vollständiges Beispiel zur Nutzung der Informationen über die Regenmenge (Station 1445, Weinheim) von wetter.com ist in {{Link2Forum|Topic=39600|Message=332837|LinkText=diesem Forenbeitrag}} beschrieben.&lt;br /&gt;
&lt;br /&gt;
== Wetter von netatmo ==&lt;br /&gt;
Das FHEM-Modul [[netatmo]] ermöglicht den Datenimport von privaten und öffentlichen netatmo Wetterstationen sowie den Abruf der in der Netatmo App verfügbaren Wettervorhersage. Eine Übersicht über die verfügbaren öffentlichen Stationen findet sich auf der Seite [http://www.netatmo.com/de-DE/weathermap Weathermap]. Die meisten öffentlichen Stationen liefern Temperatur, Luftfeuchte und Luftdruck, einige auch Wind, Regen und Daten zur Luftqualität (CO&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* {{Link2Forum|Topic=33610|LinkText=Forenthema}} mit Sammlung und Beipielen von Internet-Links für die Anzeige von teilweise animierten Wetterbildern&lt;br /&gt;
* [http://bloglich.de/art/rpi/wetterdaten-auf-dem-raspberry-pi-in-fhem/ Beispiel in Blog(de)]&lt;br /&gt;
* [[HTTPMOD]]       &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]   &lt;br /&gt;
[[Kategorie:HOWTOS]]   &lt;br /&gt;
[[Kategorie:Wetterstationen]]&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=CDCOpenData&amp;diff=38709</id>
		<title>CDCOpenData</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=CDCOpenData&amp;diff=38709"/>
		<updated>2023-11-10T17:54:54Z</updated>

		<summary type="html">&lt;p&gt;Alkazaa: /* Vorbereitung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Bitte nach &amp;quot;Anleitung&amp;quot; [[Vorlage:Infobox_Modul]] vervollständigen&lt;br /&gt;
|ModType=x&lt;br /&gt;
|ModCmdRef= &lt;br /&gt;
|ModForumArea=&lt;br /&gt;
|ModFTopic=&lt;br /&gt;
|ModTechName=&lt;br /&gt;
|ModOwner=&lt;br /&gt;
}}&lt;br /&gt;
&#039;&#039;&#039;Diese Seite ist noch im Aufbau&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der DWD stellt auf den [https://opendata.dwd.de/climate_environment/CDC/grids_germany/daily/radolan/ ftp-Seiten seines Climate Data Centers (CDC)] Werte der pro Tag gefallenen Regenmengen zur Verfügung, die auf Regenradar-Messungen beruhen und deren Werte an die gemessenen Mengen der Wetterstation angeeicht wurden. Die räumliche Auflösung beträgt dabei 1 km, was die Daten für diejenigen interessant macht, die keine eigene Regenmengenmessung zur Verfügung haben.&lt;br /&gt;
&lt;br /&gt;
Mit der gleichen räumlichen Auflösung sind auch Daten der vorhergesagten Niederschläge der nächsten 120 Minuten verfügbar. Es sind die gleichen Daten, die z.B. auch im Regenradarfilm der [https://www.dwd.de/DE/leistungen/warnwetterapp/warnwetterapp.html DWD WarnWetter-App] als Film dargestellt werden.&lt;br /&gt;
&lt;br /&gt;
Mit dem Modul 98_CDCOpenData.pm kann auf diese Daten zugegriffen werden. Zur Zeit liefert das Modul folgende Daten:&lt;br /&gt;
&lt;br /&gt;
* die Regenmenge der letzten N Tage (N konfigurierbar, default 5); update beim DWD täglich&lt;br /&gt;
* die Regenmenge seit Mitternacht; update stündlich&lt;br /&gt;
* die prognostizierten Regenintensitäten der nächsten 120 Minuten; update alle 5 Minuten&lt;br /&gt;
&lt;br /&gt;
Die Werte werden für den akuellen &#039;Home&#039;-Ort (soweit in global per latitude/longitude definiert) und für weitere konfigurierbare Orte in Deutschland und einigen angrenzenden Bereichen von Nachbarländern geliefert.&lt;br /&gt;
&lt;br /&gt;
==Anwendung==&lt;br /&gt;
Aufgrund der unterschiedlichen vom DWD bereitgestellten Datenformate benötigt das Modul zur Dekodierung u,U, zusätzliche Perl-Module, die nicht standardmäßig vorinstalliert sind und je nach Systemkonfiguration nachinstalliert werden müssen. &lt;br /&gt;
&lt;br /&gt;
===Vorbereitung ===&lt;br /&gt;
Da Modul wird über den regulären FHEM-update Prozess bereitgestellt&lt;br /&gt;
&lt;br /&gt;
===Define ===&lt;br /&gt;
&amp;lt;code&amp;gt;define &amp;lt;devicename&amp;gt; CDC OpenData&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;name&amp;gt; enableDWDdata &amp;lt;rainByDay, rainSinceMidnight, rainRadarbyLocation&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wählt aus, welche Daten periodisch abgefragt werden. Standardmäßig ist nichts ausgewählt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;name&amp;gt; INTERVAL 300&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Legt das Abfrage-Intervall fest. Der Standardwert ist 300 (Sekunden). Kleinster möglicher Wert ist 60. Wenn das Attribut cronTime gesetzt ist, wird INTERVAL deaktiviert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;name&amp;gt; cronTime &amp;lt;* * * * *&amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Falls gesetzt, bestimmt der cron-Ausdruck das Abfrageintervall. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;name&amp;gt; locations &amp;lt;[name:]latitude,longitude&amp;gt; [name:]latitude,longitude ...&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Durch Leerzeichen getrennte Liste weiterer Orte, für die Daten zusätzlich zum Standard-Ort abgefragt werden. &amp;lt;name[:]&amp;gt; ist ist optional der &amp;quot;sprechende&amp;quot; Name des Ortes. &lt;br /&gt;
&lt;br /&gt;
==Anwendungsbeispiele==&lt;br /&gt;
===Beispiel zur Modul-Einrichtung===&lt;/div&gt;</summary>
		<author><name>Alkazaa</name></author>
	</entry>
</feed>